{
 "cells": [
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "JHSdxPJuN4x7",
    "outputId": "a74dc274-dd24-4415-a3e4-1fcff4e437a2",
    "ExecuteTime": {
     "end_time": "2025-02-05T10:59:03.500175Z",
     "start_time": "2025-02-05T10:58:42.193650Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "\n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n",
    "torch.manual_seed(seed)\n",
    "torch.cuda.manual_seed_all(seed)\n",
    "np.random.seed(seed)\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "cell_type": "code",
   "source": [
    "#挂载谷歌云盘\n",
    "\n",
    "# from google.colab import drive\n",
    "# drive.mount('/content/drive')"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "yQQEArYqWylq",
    "outputId": "bc80eeee-442a-4ff2-f69e-348314f84bce",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:22.541540Z",
     "start_time": "2024-08-05T08:06:22.530536400Z"
    }
   },
   "execution_count": 3,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# !cp /content/drive/MyDrive/transformer-de-en/* . -r"
   ],
   "metadata": {
    "id": "wzS4AimwWz7f",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:22.552601Z",
     "start_time": "2024-08-05T08:06:22.536531800Z"
    }
   },
   "execution_count": 4,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "55aNBForN4x9"
   },
   "source": [
    "## 数据加载\n",
    "\n",
    "- 采用WMT16的德语和英语平行语料库，数据集主页：[WMT16](https://www.statmt.org/wmt16/multimodal-task.html#task1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "SkjBEbr1N4x-",
    "outputId": "4b9817d0-616b-41a0-ceb7-9ff6bf0f42a1",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:22.608302700Z",
     "start_time": "2024-08-05T08:06:22.548590200Z"
    }
   },
   "outputs": [],
   "source": [
    "#和jieba分词类似\n",
    "# !pip install sacremoses\n",
    "# !pip install subword-nmt\n",
    "# # BPE分词\n"
   ]
  },
  {
   "cell_type": "code",
   "source": "!sh data_multi30k.sh wmt16 wmt16_cut de en  # ＄1 ＄2 ＄3 ＄4",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Tc8aCQX-XrGv",
    "outputId": "67964082-2737-4d47-dd66-455e4ae69660",
    "ExecuteTime": {
     "end_time": "2025-02-05T11:25:24.087132Z",
     "start_time": "2025-02-05T11:24:53.079348Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[train] 源语言文本分词完成\n",
      "[train] 目标语言文本分词完成\n",
      "[val] 源语言文本分词完成\n",
      "[val] 目标语言文本分词完成\n",
      "[test] 源语言文本分词完成\n",
      "[test] 目标语言文本分词完成\n",
      "Finished applying bpe to train files.\n",
      "Finished applying bpe to val files.\n",
      "Finished applying bpe to test files.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n",
      "  0%|          | 0/20000 [00:00<?, ?it/s]\n",
      "  0%|          | 8/20000 [00:00<04:17, 77.67it/s]\n",
      "  0%|          | 21/20000 [00:00<03:24, 97.49it/s]\n",
      "  0%|          | 45/20000 [00:00<02:09, 154.10it/s]\n",
      "  0%|          | 82/20000 [00:00<01:25, 232.85it/s]\n",
      "  1%|          | 132/20000 [00:00<01:01, 322.02it/s]\n",
      "  1%|          | 185/20000 [00:00<00:51, 388.04it/s]\n",
      "  1%|1         | 252/20000 [00:00<00:41, 476.07it/s]\n",
      "  2%|1         | 320/20000 [00:00<00:36, 538.67it/s]\n",
      "  2%|1         | 397/20000 [00:00<00:32, 609.05it/s]\n",
      "  2%|2         | 473/20000 [00:01<00:29, 651.93it/s]\n",
      "  3%|2         | 559/20000 [00:01<00:27, 712.18it/s]\n",
      "  3%|3         | 645/20000 [00:01<00:25, 755.54it/s]\n",
      "  4%|3         | 742/20000 [00:01<00:23, 816.17it/s]\n",
      "  4%|4         | 843/20000 [00:01<00:21, 873.35it/s]\n",
      "  5%|4         | 955/20000 [00:01<00:20, 944.82it/s]\n",
      "  5%|5         | 1064/20000 [00:01<00:19, 987.55it/s]\n",
      "  6%|5         | 1183/20000 [00:01<00:18, 1043.93it/s]\n",
      "  7%|6         | 1306/20000 [00:01<00:17, 1099.13it/s]\n",
      "  7%|7         | 1421/20000 [00:01<00:16, 1113.93it/s]\n",
      "  8%|7         | 1569/20000 [00:02<00:15, 1221.80it/s]\n",
      "  9%|8         | 1712/20000 [00:02<00:14, 1282.57it/s]\n",
      "  9%|9         | 1882/20000 [00:02<00:12, 1404.45it/s]\n",
      " 10%|#         | 2056/20000 [00:02<00:11, 1502.14it/s]\n",
      " 11%|#1        | 2258/20000 [00:02<00:10, 1653.53it/s]\n",
      " 12%|#2        | 2473/20000 [00:02<00:09, 1797.10it/s]\n",
      " 14%|#3        | 2743/20000 [00:02<00:08, 2063.82it/s]\n",
      " 15%|#5        | 3054/20000 [00:02<00:07, 2371.45it/s]\n",
      " 17%|#7        | 3412/20000 [00:02<00:06, 2730.28it/s]\n",
      " 19%|#8        | 3761/20000 [00:03<00:07, 2313.55it/s]\n",
      " 20%|##        | 4007/20000 [00:03<00:09, 1686.67it/s]\n",
      " 21%|##1       | 4208/20000 [00:03<00:11, 1421.08it/s]\n",
      " 22%|##1       | 4377/20000 [00:03<00:11, 1315.82it/s]\n",
      " 23%|##2       | 4527/20000 [00:03<00:12, 1197.64it/s]\n",
      " 23%|##3       | 4659/20000 [00:03<00:13, 1141.15it/s]\n",
      " 24%|##3       | 4781/20000 [00:04<00:13, 1122.65it/s]\n",
      " 24%|##4       | 4898/20000 [00:04<00:13, 1131.84it/s]\n",
      " 25%|##5       | 5015/20000 [00:04<00:14, 1057.60it/s]\n",
      " 26%|##5       | 5142/20000 [00:04<00:13, 1108.00it/s]\n",
      " 26%|##6       | 5270/20000 [00:04<00:12, 1150.65it/s]\n",
      " 27%|##6       | 5397/20000 [00:04<00:12, 1182.47it/s]\n",
      " 28%|##7       | 5526/20000 [00:04<00:11, 1211.84it/s]\n",
      " 28%|##8       | 5658/20000 [00:04<00:11, 1241.52it/s]\n",
      " 29%|##9       | 5805/20000 [00:04<00:10, 1307.06it/s]\n",
      " 30%|##9       | 5959/20000 [00:05<00:10, 1371.06it/s]\n",
      " 31%|###       | 6118/20000 [00:05<00:09, 1433.32it/s]\n",
      " 31%|###1      | 6280/20000 [00:05<00:09, 1488.27it/s]\n",
      " 32%|###2      | 6454/20000 [00:05<00:08, 1558.55it/s]\n",
      " 33%|###3      | 6652/20000 [00:05<00:07, 1682.15it/s]\n",
      " 34%|###4      | 6875/20000 [00:05<00:07, 1840.19it/s]\n",
      " 36%|###5      | 7109/20000 [00:05<00:06, 1984.76it/s]\n",
      " 37%|###6      | 7374/20000 [00:05<00:05, 2178.83it/s]\n",
      " 38%|###8      | 7663/20000 [00:05<00:05, 2384.70it/s]\n",
      " 40%|####      | 8033/20000 [00:05<00:04, 2767.59it/s]\n",
      " 42%|####1     | 8328/20000 [00:06<00:05, 2091.04it/s]\n",
      " 43%|####2     | 8563/20000 [00:06<00:07, 1621.29it/s]\n",
      " 44%|####3     | 8757/20000 [00:06<00:07, 1413.45it/s]\n",
      " 45%|####4     | 8923/20000 [00:06<00:08, 1292.98it/s]\n",
      " 45%|####5     | 9069/20000 [00:06<00:08, 1226.90it/s]\n",
      " 46%|####6     | 9203/20000 [00:07<00:09, 1177.85it/s]\n",
      " 47%|####6     | 9328/20000 [00:07<00:09, 1180.49it/s]\n",
      " 47%|####7     | 9451/20000 [00:07<00:09, 1170.64it/s]\n",
      " 48%|####7     | 9586/20000 [00:07<00:08, 1213.49it/s]\n",
      " 49%|####8     | 9718/20000 [00:07<00:08, 1241.16it/s]\n",
      " 49%|####9     | 9845/20000 [00:07<00:08, 1237.27it/s]\n",
      " 50%|####9     | 9971/20000 [00:07<00:08, 1242.30it/s]\n",
      " 51%|#####     | 10113/20000 [00:07<00:07, 1292.13it/s]\n",
      " 51%|#####1    | 10279/20000 [00:07<00:06, 1398.08it/s]\n",
      " 52%|#####2    | 10446/20000 [00:07<00:06, 1475.00it/s]\n",
      " 53%|#####3    | 10602/20000 [00:08<00:06, 1499.15it/s]\n",
      " 54%|#####3    | 10780/20000 [00:08<00:05, 1577.92it/s]\n",
      " 55%|#####4    | 10976/20000 [00:08<00:05, 1690.02it/s]\n",
      " 56%|#####5    | 11190/20000 [00:08<00:04, 1822.05it/s]\n",
      " 57%|#####7    | 11444/20000 [00:08<00:04, 2028.93it/s]\n",
      " 59%|#####8    | 11701/20000 [00:08<00:03, 2187.16it/s]\n",
      " 60%|######    | 12026/20000 [00:08<00:03, 2501.52it/s]\n",
      " 62%|######2   | 12474/20000 [00:08<00:02, 3080.11it/s]\n",
      " 64%|######3   | 12782/20000 [00:09<00:04, 1582.95it/s]\n",
      " 65%|######5   | 13020/20000 [00:09<00:05, 1307.48it/s]\n",
      " 66%|######6   | 13213/20000 [00:09<00:05, 1190.28it/s]\n",
      " 67%|######6   | 13375/20000 [00:09<00:05, 1162.26it/s]\n",
      " 68%|######7   | 13521/20000 [00:09<00:05, 1138.04it/s]\n",
      " 68%|######8   | 13655/20000 [00:10<00:05, 1144.06it/s]\n",
      " 69%|######8   | 13784/20000 [00:10<00:05, 1157.75it/s]\n",
      " 70%|######9   | 13911/20000 [00:10<00:05, 1177.89it/s]\n",
      " 70%|#######   | 14040/20000 [00:10<00:04, 1202.51it/s]\n",
      " 71%|#######   | 14173/20000 [00:10<00:04, 1234.68it/s]\n",
      " 72%|#######1  | 14309/20000 [00:10<00:04, 1266.65it/s]\n",
      " 72%|#######2  | 14456/20000 [00:10<00:04, 1320.49it/s]\n",
      " 73%|#######3  | 14601/20000 [00:10<00:03, 1357.06it/s]\n",
      " 74%|#######3  | 14753/20000 [00:10<00:03, 1401.67it/s]\n",
      " 74%|#######4  | 14897/20000 [00:10<00:03, 1410.42it/s]\n",
      " 75%|#######5  | 15056/20000 [00:11<00:03, 1459.24it/s]\n",
      " 76%|#######6  | 15217/20000 [00:11<00:03, 1501.30it/s]\n",
      " 77%|#######6  | 15388/20000 [00:11<00:02, 1562.81it/s]\n",
      " 78%|#######7  | 15588/20000 [00:11<00:02, 1690.79it/s]\n",
      " 79%|#######8  | 15758/20000 [00:11<00:02, 1681.05it/s]\n",
      " 80%|#######9  | 15977/20000 [00:11<00:02, 1827.88it/s]\n",
      " 81%|########1 | 16243/20000 [00:11<00:01, 2068.20it/s]\n",
      " 83%|########2 | 16531/20000 [00:11<00:01, 2304.24it/s]\n",
      " 84%|########3 | 16774/20000 [00:11<00:01, 2335.75it/s]\n",
      " 86%|########5 | 17105/20000 [00:11<00:01, 2623.06it/s]\n",
      " 87%|########6 | 17368/20000 [00:13<00:04, 549.96it/s] \n",
      " 88%|########7 | 17558/20000 [00:14<00:06, 355.58it/s]\n",
      " 88%|########8 | 17696/20000 [00:14<00:06, 357.18it/s]\n",
      " 89%|########9 | 17804/20000 [00:15<00:05, 374.07it/s]\n",
      " 89%|########9 | 17894/20000 [00:15<00:05, 405.88it/s]\n",
      " 90%|########9 | 17977/20000 [00:15<00:04, 440.79it/s]\n",
      " 90%|######### | 18056/20000 [00:15<00:04, 480.75it/s]\n",
      " 91%|######### | 18134/20000 [00:15<00:03, 518.18it/s]\n",
      " 91%|#########1| 18210/20000 [00:15<00:03, 556.66it/s]\n",
      " 91%|#########1| 18290/20000 [00:15<00:02, 604.00it/s]\n",
      " 92%|#########1| 18368/20000 [00:15<00:02, 642.16it/s]\n",
      " 92%|#########2| 18445/20000 [00:15<00:02, 656.07it/s]\n",
      " 93%|#########2| 18520/20000 [00:16<00:02, 674.05it/s]\n",
      " 93%|#########2| 18594/20000 [00:16<00:02, 672.47it/s]\n",
      " 93%|#########3| 18674/20000 [00:16<00:01, 704.95it/s]\n",
      " 94%|#########3| 18757/20000 [00:16<00:01, 735.86it/s]\n",
      " 94%|#########4| 18840/20000 [00:16<00:01, 759.14it/s]\n",
      " 95%|#########4| 18928/20000 [00:16<00:01, 793.24it/s]\n",
      " 95%|#########5| 19017/20000 [00:16<00:01, 819.80it/s]\n",
      " 96%|#########5| 19102/20000 [00:16<00:01, 826.62it/s]\n",
      " 96%|#########5| 19186/20000 [00:16<00:00, 829.69it/s]\n",
      " 96%|#########6| 19284/20000 [00:16<00:00, 872.56it/s]\n",
      " 97%|#########6| 19383/20000 [00:17<00:00, 907.20it/s]\n",
      " 97%|#########7| 19492/20000 [00:17<00:00, 958.96it/s]\n",
      " 98%|#########7| 19591/20000 [00:17<00:00, 966.05it/s]\n",
      " 98%|#########8| 19700/20000 [00:17<00:00, 1002.44it/s]\n",
      " 99%|#########9| 19812/20000 [00:17<00:00, 1034.66it/s]\n",
      "100%|#########9| 19934/20000 [00:17<00:00, 1087.21it/s]\n",
      "100%|##########| 20000/20000 [00:17<00:00, 1132.76it/s]\n"
     ]
    }
   ],
   "execution_count": 2
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Lvk4q0-1N4x-"
   },
   "source": [
    "Dataset"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "zCXKEwspN4x_",
    "outputId": "3dcce6fa-932f-4b22-9dc8-398a4b402733",
    "ExecuteTime": {
     "end_time": "2025-02-05T12:18:28.759118Z",
     "start_time": "2025-02-05T12:18:28.726243Z"
    }
   },
   "source": [
    "from pathlib import Path\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "\n",
    "\n",
    "\n",
    "class LangPairDataset(Dataset):\n",
    "\n",
    "    def __init__(\n",
    "        self, mode=\"train\", max_length=128, overwrite_cache=False, data_dir=\"wmt16\",\n",
    "    ):   # overwrite_cache 是否覆盖缓存文件\n",
    "        self.data_dir = Path(data_dir)\n",
    "        cache_path = self.data_dir / \".cache\" / f\"de2en_{mode}_{max_length}.npy\"\n",
    "\n",
    "        if overwrite_cache or not cache_path.exists():\n",
    "            cache_path.parent.mkdir(parents=True, exist_ok=True) # 创建缓存目录\n",
    "\n",
    "            with open(self.data_dir / f\"{mode}_src.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "                self.src = file.readlines() # 读取源语言文件所有行\n",
    "\n",
    "            with open(self.data_dir / f\"{mode}_trg.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "                self.trg = file.readlines() # 读取目标语言文件所有行，self.trg和self.src都是长长的字符串\n",
    "\n",
    "            filtered_src = []\n",
    "            filtered_trg = []\n",
    "            # max length filter,超出最大长度的句子舍弃\n",
    "            for src, trg in zip(self.src, self.trg):\n",
    "                if len(src) <= max_length and len(trg) <= max_length: # 过滤长度超过最大长度的句子\n",
    "                    filtered_src.append(src.strip()) # 去掉句子前后的空格\n",
    "                    filtered_trg.append(trg.strip())\n",
    "            filtered_src = np.array(filtered_src)\n",
    "            filtered_trg = np.array(filtered_trg)\n",
    "            np.save(\n",
    "                cache_path,\n",
    "                {\"src\": filtered_src, \"trg\": filtered_trg },\n",
    "                allow_pickle=True,\n",
    "            )#allow_pickle=True允许保存对象数组，将过滤后的数据保存为 NumPy 数组，存储在缓存文件中\n",
    "            print(f\"save cache to {cache_path}\")\n",
    "\n",
    "        else:  # 读取缓存文件\n",
    "            cache_dict = np.load(cache_path, allow_pickle=True).item() #allow_pickle=True允许保存对象数组\n",
    "            print(f\"load {mode} dataset from {cache_path}\")\n",
    "            filtered_src = cache_dict[\"src\"]\n",
    "            filtered_trg = cache_dict[\"trg\"]\n",
    "\n",
    "        self.src = filtered_src  # 将过滤后的数据赋值给self.src\n",
    "        self.trg = filtered_trg  # 将过滤后的数据赋值给self.trg\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        return self.src[index], self.trg[index]\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.src)\n",
    "\n",
    "\n",
    "train_ds = LangPairDataset(\"train\")\n",
    "val_ds = LangPairDataset(\"val\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load train dataset from wmt16\\.cache\\de2en_train_128.npy\n",
      "load val dataset from wmt16\\.cache\\de2en_val_128.npy\n"
     ]
    }
   ],
   "execution_count": 18
  },
  {
   "cell_type": "code",
   "source": [
    "# 清除缓存\n",
    "# !rm wmt16/.cache -r"
   ],
   "metadata": {
    "id": "yHB9TDpDQlv2",
    "ExecuteTime": {
     "end_time": "2025-02-05T11:56:32.094972Z",
     "start_time": "2025-02-05T11:56:32.090456Z"
    }
   },
   "outputs": [],
   "execution_count": 8
  },
  {
   "cell_type": "code",
   "source": [
    "len(train_ds) #少了1000多个样本"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "meHHXL3MN4x_",
    "outputId": "a001c071-7021-4dff-8bc0-3e5da01d7210",
    "ExecuteTime": {
     "end_time": "2025-02-05T11:52:07.896931Z",
     "start_time": "2025-02-05T11:52:07.813849Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "27962"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 4
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-05T11:52:19.367402Z",
     "start_time": "2025-02-05T11:52:19.359741Z"
    }
   },
   "cell_type": "code",
   "source": "len(val_ds) #少了100多个样本",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "966"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 5
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "LZvJV37NN4x_",
    "outputId": "43909dbd-cf15-447b-e8b6-a07de381ad0a",
    "ExecuteTime": {
     "end_time": "2025-02-05T11:54:29.278173Z",
     "start_time": "2025-02-05T11:54:29.271581Z"
    }
   },
   "source": "print(\"source: {}\\ntarget: {}\".format(*train_ds[0]))",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "source: zwei junge weiße männer sind im freien in der nähe vieler büsche .\n",
      "target: two young , white males are outside near many bushes .\n"
     ]
    }
   ],
   "execution_count": 7
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TE8gRzYQN4yA"
   },
   "source": [
    "### Tokenizer\n",
    "\n",
    "这里有两种处理方式，分别对应着 encoder 和 decoder 的 word embedding 是否共享，这里实现共享的方案"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 66,
     "referenced_widgets": [
      "7190c928edb040c3a523cdd2e3f45572",
      "990741ff083d457587665d8f620b69c3",
      "00fe6a550df547cea5b1e0d84c1a3f5f",
      "fab8b1e3187841ee894b92859fa4821b",
      "f9b0d916e1a64c2a9f384e42f3d5dcab",
      "a78199650ecc47bd81f482a2671b563a",
      "673bd3773a374261a01cfc48081c27f3",
      "83f345f7424c43929d7a304cbfb6d0fe",
      "58d69d086f644e89937347e1f9261d6f",
      "8cf462fa996c41b18fb4571e0a757363",
      "434ac4c51db74681942d2d3bd869b1df"
     ]
    },
    "id": "yAmlq_9YN4yA",
    "outputId": "263a89f7-9bee-4c6d-cf7a-7d82b0a81f92",
    "ExecuteTime": {
     "end_time": "2025-02-05T12:53:30.763060Z",
     "start_time": "2025-02-05T12:53:30.504782Z"
    }
   },
   "source": [
    "#载入词表，看下词表长度，词表就像英语字典,构建word2idx和idx2word\n",
    "word2idx = {\n",
    "    \"[PAD]\": 0,     # 填充 token\n",
    "    \"[BOS]\": 1,     # begin of sentence\n",
    "    \"[UNK]\": 2,     # 未知 token\n",
    "    \"[EOS]\": 3,     # end of sentence\n",
    "}\n",
    "idx2word = {value: key for key, value in word2idx.items()}\n",
    "index = len(idx2word)\n",
    "threshold = 1  # 出现次数低于此的token舍弃\n",
    "\n",
    "with open(\"wmt16/vocab\", \"r\", encoding=\"utf8\") as file:\n",
    "    for line in tqdm(file.readlines()):\n",
    "        token, counts = line.strip().split()\n",
    "        if int(counts) >= threshold:\n",
    "            word2idx[token] = index  # 词变id的过程称为变成token的过程\n",
    "            idx2word[index] = token  \n",
    "            index += 1\n",
    "\n",
    "vocab_size = len(word2idx)\n",
    "print(\"vocab_size: {}\".format(vocab_size))"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  0%|          | 0/18107 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "733861da410241f2b0e9e494b0004196"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "vocab_size: 18111\n"
     ]
    }
   ],
   "execution_count": 19
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "e_ed0jmJN4yA",
    "outputId": "407a9e69-4981-42ae-c7a9-aa4c701185ef",
    "ExecuteTime": {
     "end_time": "2025-02-05T13:31:09.135132Z",
     "start_time": "2025-02-05T13:31:08.206900Z"
    }
   },
   "source": [
    "class Tokenizer:\n",
    "    def __init__(self, word2idx, idx2word, max_length=128, pad_idx=0, bos_idx=1, eos_idx=3, unk_idx=2):\n",
    "        self.word2idx = word2idx\n",
    "        self.idx2word = idx2word\n",
    "        self.max_length = max_length\n",
    "        self.pad_idx = pad_idx\n",
    "        self.bos_idx = bos_idx\n",
    "        self.eos_idx = eos_idx\n",
    "        self.unk_idx = unk_idx\n",
    "\n",
    "    def encode(self, text_list, padding_first=False, add_bos=True, add_eos=True, return_mask=False):\n",
    "        \"\"\"如果padding_first == True，则padding加载前面，否则加载后面\n",
    "        将文本列表编码为索引序列，并可选择添加开始符号、结束符号和填充符号，同时可返回填充掩码。\n",
    "        \"\"\"\n",
    "        max_length = min(self.max_length, add_eos + add_bos + max([len(text) for text in text_list]))\n",
    "        indices_list = []\n",
    "        for text in text_list:\n",
    "            indices = [self.word2idx.get(word, self.unk_idx) for word in text[:max_length - add_bos - add_eos]]\n",
    "            if add_bos:\n",
    "                indices = [self.bos_idx] + indices\n",
    "            if add_eos:\n",
    "                indices = indices + [self.eos_idx]\n",
    "            if padding_first:\n",
    "                indices = [self.pad_idx] * (max_length - len(indices)) + indices\n",
    "            else:\n",
    "                indices = indices + [self.pad_idx] * (max_length - len(indices))\n",
    "            indices_list.append(indices)\n",
    "        input_ids = torch.tensor(indices_list)\n",
    "        masks = (input_ids == self.pad_idx).to(dtype=torch.int64) # 为了方便损失计算，这里的mask为0的地方需要计算，为1的地方不需要计算,即padding的地方为1,非padding的地方为0\n",
    "        return input_ids if not return_mask else (input_ids, masks)\n",
    "\n",
    "\n",
    "    def decode(self, indices_list, remove_bos=True, remove_eos=True, remove_pad=True, split=False):\n",
    "        \"\"\"\n",
    "        将索引序列解码为文本。\n",
    "        \"\"\"\n",
    "        text_list = []\n",
    "        for indices in indices_list:\n",
    "            text = []\n",
    "            for index in indices:\n",
    "                word = self.idx2word.get(index, \"[UNK]\")\n",
    "                if remove_bos and word == \"[BOS]\":\n",
    "                    continue\n",
    "                if remove_eos and word == \"[EOS]\":\n",
    "                    break\n",
    "                if remove_pad and word == \"[PAD]\":\n",
    "                    break\n",
    "                text.append(word)\n",
    "            text_list.append(\" \".join(text) if not split else text)\n",
    "        return text_list\n",
    "\n",
    "# 与seq2seq不同，这里的tokenizer只有一个\n",
    "tokenizer = Tokenizer(word2idx=word2idx, idx2word=idx2word)\n",
    "\n",
    "\n",
    "raw_text = [\"hello world\".split(), \"tokenize text datas with batch\".split(), \"this is a test\".split()]\n",
    "indices = tokenizer.encode(raw_text, padding_first=False, add_bos=True, add_eos=True)\n",
    "decode_text = tokenizer.decode(indices.tolist(), remove_bos=False, remove_eos=False, remove_pad=False)\n",
    "print(\"raw text\")\n",
    "for raw in raw_text:\n",
    "    print(raw)\n",
    "print(\"indices\")\n",
    "for index in indices:\n",
    "    print(index)\n",
    "print(\"decode text\")\n",
    "for decode in decode_text:\n",
    "    print(decode)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "raw text\n",
      "['hello', 'world']\n",
      "['tokenize', 'text', 'datas', 'with', 'batch']\n",
      "['this', 'is', 'a', 'test']\n",
      "indices\n",
      "tensor([   1, 9458, 3522,    3,    0,    0,    0])\n",
      "tensor([   1,    2, 5463,    2,   22,    2,    3])\n",
      "tensor([   1,  385,   18,    5, 5699,    3,    0])\n",
      "decode text\n",
      "[BOS] hello world [EOS] [PAD] [PAD] [PAD]\n",
      "[BOS] [UNK] text [UNK] with [UNK] [EOS]\n",
      "[BOS] this is a test [EOS] [PAD]\n"
     ]
    }
   ],
   "execution_count": 20
  },
  {
   "cell_type": "code",
   "source": [
    "for i,j in train_ds:\n",
    "    print(len(i))\n",
    "    print(len(j))\n",
    "    break\n",
    "print(\"source: {}\\ntarget: {}\".format(*train_ds[0]))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T03:21:32.452103Z",
     "start_time": "2025-02-06T03:21:32.446141Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "66\n",
      "54\n",
      "source: zwei junge weiße männer sind im freien in der nähe vieler büsche .\n",
      "target: two young , white males are outside near many bushes .\n"
     ]
    }
   ],
   "execution_count": 26
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ftcEnnKxN4yB"
   },
   "source": [
    "### Transformer Batch Sampler\n",
    "\n",
    "> Sentence pairs were batched together by approximate sequence length. Each training batch contained a set of sentence pairs containing approximately 25000 source tokens and 25000 target tokens\n",
    "句子按照序列长度差不多的分到一个批次。 每个训练批次包含一组句子对，其中包含大约 25000 个源标记和 25000 个目标标记"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "qP8nznIuN4yB",
    "ExecuteTime": {
     "end_time": "2025-02-06T04:06:54.966680Z",
     "start_time": "2025-02-06T04:06:54.947825Z"
    }
   },
   "source": [
    "class SampleInfo: #下面的info对象\n",
    "    def __init__(self, i, lens):\n",
    "        \"\"\"\n",
    "        记录文本对的序号和长度信息\n",
    "        输入：\n",
    "            - i (int): 文本对的序号。\n",
    "            - lens (list): 文本对源语言和目标语言的长度\n",
    "        \"\"\"\n",
    "        self.i = i\n",
    "        # 加一是考虑填补在文本前后的特殊词元([BOS],[EOS],[PAD])，lens[0]和lens[1]分别表示源语言和目标语言的长度\n",
    "        self.max_len = max(lens[0], lens[1]) + 1\n",
    "        self.src_len = lens[0] + 1\n",
    "        self.trg_len = lens[1] + 1\n",
    "\n",
    "# 一个批量生成器，根据词元数目的限制来控制批量的大小。它会根据传入的样本信息，在不超过设定大小的情况下，逐步构建批量。\n",
    "class TokenBatchCreator:\n",
    "    def __init__(self, batch_size):\n",
    "        \"\"\"\n",
    "        参数:\n",
    "        batch_size (int): 用于限制批量的大小。\n",
    "        功能:\n",
    "        初始化了一个空的批量列表 _batch。\n",
    "        设定了初始的最大长度为 -1。\n",
    "        存储了传入的 batch_size。\n",
    "        \"\"\"\n",
    "\n",
    "        self._batch = []  #这个就是之前的batch_size，就是第一个batch内有多少个样本\n",
    "        self.max_len = -1\n",
    "        self._batch_size = batch_size # 限制批量的大小,假设是4096 可理解为一个面积的上限，“长” 表示当前批量中样本的最大长度，“宽” 表示批量中样本的数量。\n",
    "\n",
    "    def append(self, info: SampleInfo):\n",
    "        \"\"\"\n",
    "        参数:\n",
    "        info (SampleInfo): 文本对的信息。\n",
    "        功能:\n",
    "        接收一个 SampleInfo 对象，并根据其最大长度信息更新当前批量的最大长度。\n",
    "        如果将新的样本加入批量后超过了批量大小限制，它会返回已有的批量并将新的样本加入新的批量。\n",
    "        否则，它会更新最大长度并将样本添加到当前批量中。\n",
    "        \"\"\"\n",
    "        # 更新当前批量的最大长度\n",
    "        cur_len = info.max_len # 当前样本的长度\n",
    "        max_len = max(self.max_len, cur_len) # 每来一个样本，更新当前批次的最大长度\n",
    "        # 如果新的样本加入批量后超过大小限制，则将已有的批量返回，新的样本加入新的批量\n",
    "        if max_len * (len(self._batch) + 1) > self._batch_size:  # self._batch实际上就是宽，每加入一个样本宽度就加一\n",
    "            self._batch, result = [], self._batch # 保存当前的batch，并返回,这里的result是之前的batch,_batch清空\n",
    "            self._batch.append(info) #箱子里的第一条样本，放入\n",
    "            self.max_len = cur_len #因为是当前batch的第一个样本，所以它的长度就是当前长度\n",
    "            return result  # result就是一个批，里面有多个样本\n",
    "        else:\n",
    "            self.max_len = max_len\n",
    "            self._batch.append(info) # 将样本添加到当前批量中\n",
    "            return None\n",
    "\n",
    "    @property #使用@property装饰器将batch方法转换为属性，通过batch属性可以直接获取当前批量的SampleInfo对象列表。\n",
    "    def batch(self):\n",
    "        return self._batch"
   ],
   "outputs": [],
   "execution_count": 27
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "_Vtc0gXEN4yB",
    "ExecuteTime": {
     "end_time": "2025-02-06T04:08:48.878538Z",
     "start_time": "2025-02-06T04:08:48.869172Z"
    }
   },
   "source": [
    "from torch.utils.data import BatchSampler\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "class TransformerBatchSampler(BatchSampler):\n",
    "    def __init__(self,\n",
    "                 dataset,\n",
    "                 batch_size,\n",
    "                 shuffle_batch=False,\n",
    "                 clip_last_batch=False,\n",
    "                 seed=0):\n",
    "        \"\"\"\n",
    "        批量采样器\n",
    "        输入:\n",
    "            - dataset: 数据集\n",
    "            - batch_size: 批量大小\n",
    "            - shuffle_batch: 是否对生成的批量进行洗牌 就是不同的batch先后顺序不一样\n",
    "            - clip_last_batch: 是否裁剪最后剩下的数据\n",
    "            - seed: 随机数种子\n",
    "        \"\"\"\n",
    "        self._dataset = dataset\n",
    "        self._batch_size = batch_size\n",
    "        self._shuffle_batch = shuffle_batch\n",
    "        self._clip_last_batch = clip_last_batch\n",
    "        self._seed = seed #下面3个是为了随机\n",
    "        self._random = np.random\n",
    "        self._random.seed(seed)\n",
    "\n",
    "        self._sample_infos = []\n",
    "        # 根据数据集中的每个样本，创建了对应的 SampleInfo 对象，包含了样本的索引和长度信息。self._sample_infos是一个列表，里面装着SampleInfo对象,即每个样本的索引和长度信息\n",
    "        for i, data in enumerate(self._dataset):\n",
    "            lens = [len(data[0]), len(data[1])] #输入和输出的长度计算放到lens中\n",
    "            self._sample_infos.append(SampleInfo(i, lens))\n",
    "\n",
    "    def __iter__(self):\n",
    "        \"\"\"\n",
    "        对数据集中的样本进行排序，排序规则是先按源语言长度排序，如果相同则按目标语言长度排序。因为同一个批次的长度相差应该不大\n",
    "        使用 TokenBatchCreator 逐步组装批量数据，当满足批量大小时返回一个批量的样本信息。\n",
    "        如果不裁剪最后一个批次的数据且存在剩余样本，则将这些样本组成最后一个批次。\n",
    "        如果需要对批量进行洗牌，则对批次进行洗牌操作。\n",
    "        通过迭代器，抛出每个批量的样本在数据集中的索引。\n",
    "        \"\"\"\n",
    "        # 排序，如果源语言长度相同则按照目标语言的长度排列\n",
    "        infos = sorted(self._sample_infos,\n",
    "                       key=lambda x: (x.src_len, x.trg_len))\n",
    "        # 把样本放入到箱子里，所有装箱后的箱子，每一个箱子都放入batch_infos\n",
    "        batch_infos = []\n",
    "        batch_creator = TokenBatchCreator(self._batch_size) # 批量生成器\n",
    "        for info in infos:\n",
    "            batch = batch_creator.append(info)\n",
    "            # 存够一个batch的样本信息后，会把这个batch返回，否则返回为None\n",
    "            if batch is not None:\n",
    "                batch_infos.append(batch)\n",
    "\n",
    "        # 是否抛弃最后批量的文本对\n",
    "        if not self._clip_last_batch and len(batch_creator.batch) != 0:\n",
    "            batch_infos.append(batch_creator.batch) # 最后一个batch\n",
    "\n",
    "        # 打乱batch，打乱的是箱子的顺序\n",
    "        if self._shuffle_batch:\n",
    "            self._random.shuffle(batch_infos)\n",
    "\n",
    "        self.batch_number = len(batch_infos)\n",
    "        # print(self.batch_number) #为了理解\n",
    "\n",
    "        # 抛出一个箱子里所有样本的序号\n",
    "        for batch in batch_infos:\n",
    "            batch_indices = [info.i for info in batch] # 批量的样本在数据集中的索引，第一个batch[0,1,.....82]，第二个batch[83,84,85,86,87]\n",
    "            yield batch_indices\n",
    "\n",
    "    def __len__(self):\n",
    "        \"\"\"\n",
    "        返回批量的数量\n",
    "        \"\"\"\n",
    "        if hasattr(self, \"batch_number\"):\n",
    "            return self.batch_number\n",
    "        # 计算批量的数量,没有用到下面的情况，不用看\n",
    "        batch_number = (len(self._dataset) +\n",
    "                        self._batch_size) // self._batch_size\n",
    "        return batch_number"
   ],
   "outputs": [],
   "execution_count": 36
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "d00AFRzVN4yC",
    "outputId": "a43c1a14-102f-4cbb-fff1-761cb46e31b3",
    "ExecuteTime": {
     "end_time": "2025-02-06T04:08:50.893199Z",
     "start_time": "2025-02-06T04:08:50.828915Z"
    }
   },
   "source": [
    "sampler = TransformerBatchSampler(train_ds, batch_size=4096, shuffle_batch=True)\n",
    "\n",
    "#为什么这里每个批量的样本对数目不一样呢？长度*batch_number>4096的时候，就会返回上一个batch，然后新的样本加入新的batch,具体要看TokenBatchCreator的44行"
   ],
   "outputs": [],
   "execution_count": 37
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T04:08:53.748704Z",
     "start_time": "2025-02-06T04:08:53.701055Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 因为写了iter方法，所以类实例化的对象就可以使用for循环  每for循环一次，就调用一次iter方法，把iter方法返回的东西拿给i\n",
    "for i in sampler:\n",
    "    print(i)\n",
    "    break"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[26845, 982, 1864, 3222, 4716, 5341, 5897, 6103, 8531, 10067, 11125, 13063, 14929, 15158, 18063, 18502, 20661, 20986, 21320, 22030, 22882, 23567, 24909, 1881, 2268, 4268, 7413, 10166, 12447, 13573, 15332, 16727, 17657, 20829, 21508, 23094, 24274, 25801, 518, 2145, 4111, 4739, 4745, 7493, 7623, 8369, 8618, 8730, 9054, 9391, 9808, 10141, 10412, 11422, 13534, 15038]\n"
     ]
    }
   ],
   "execution_count": 38
  },
  {
   "cell_type": "code",
   "source": [
    "for idx, batch in enumerate(sampler):\n",
    "    print(\"第{}批量的数据中含有文本对是：{}，数量为：{}\".format(idx, batch, len(batch)))\n",
    "    if idx >= 3:\n",
    "        break"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T04:08:55.725268Z",
     "start_time": "2025-02-06T04:08:55.671214Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第0批量的数据中含有文本对是：[21271, 22083, 25959, 49, 637, 6773, 8687, 16391, 19042, 22788, 22867, 27194, 2246, 11350, 12499, 14881, 16713, 20199, 17560, 23179, 6216, 18973, 19368, 25486, 15211, 17452, 17890, 19236, 25288, 27115, 2627, 19805, 755, 10914, 16360, 21301, 27624, 7114]，数量为：38\n",
      "第1批量的数据中含有文本对是：[25919, 26342, 1164, 1463, 4572, 4601, 4923, 5629, 10667, 12481, 13313, 13485, 15766, 16256, 19927, 20233, 21061, 27843, 1040, 2768, 4096, 5842, 5847, 6265, 6663, 8346, 8481, 10076, 14832, 17516, 18001, 21196, 22517, 23172, 23181, 23604, 27227, 27892, 3, 120, 607, 2870, 3270, 9536, 10044, 11085, 15960, 21744, 22156, 22189, 24491, 27443, 151]，数量为：53\n",
      "第2批量的数据中含有文本对是：[23598, 26090, 26756, 27395, 6244, 10461, 12839, 16355, 20300, 21417, 26565, 4264, 4307, 6294, 7085, 9525, 9559, 9647, 9710, 10344, 12214, 13075, 13337, 13691, 16381, 19796, 21830, 23531, 25014, 25921, 27752, 1850, 4221, 5306, 8341, 8859, 9052, 10025, 11858, 11948, 15347, 15812, 18069, 18469, 22704, 23203, 23537, 26233, 26928, 27214, 179, 1745, 4982]，数量为：53\n",
      "第3批量的数据中含有文本对是：[17510, 26533, 12808, 1291, 11297, 19038, 24464, 1494, 3332, 3655, 15580, 24842, 1275, 7737, 16751, 16762, 21651, 24549, 25529, 26103, 4049, 4332, 23891, 24659, 25552, 27437, 1112, 6020, 24492, 14171, 17259, 19267, 26181, 26556, 2919, 3983, 998, 26712, 24446]，数量为：39\n"
     ]
    }
   ],
   "execution_count": 39
  },
  {
   "cell_type": "code",
   "source": "len(sampler)  #批次的数量",
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T04:09:52.439862Z",
     "start_time": "2025-02-06T04:09:52.433929Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "527"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 40
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Olkaw4JNN4yC"
   },
   "source": [
    "### DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "rvvuNJIzN4yC",
    "ExecuteTime": {
     "end_time": "2025-02-06T04:13:46.220334Z",
     "start_time": "2025-02-06T04:13:46.212846Z"
    }
   },
   "source": [
    "def collate_fct(batch, tokenizer):\n",
    "    \"\"\"\n",
    "    将一批源语言和目标语言的文本数据进行编码、添加特殊符号和填充处理，并生成相应的掩码，最终将处理好的数据以字典的形式返回，以便输入到序列到序列（Seq2Seq）模型中进行训练。\n",
    "    \"\"\"\n",
    "    src_words = [pair[0].split() for pair in batch]\n",
    "    trg_words = [pair[1].split() for pair in batch]\n",
    "\n",
    "    # [BOS] src [EOS] [PAD]  seq2seq里面padding_first=True是因为那边是串行训练，要让有效的信息离decoder更近，所以padding_first=True  而这边是并行计算的，所以padding_first=False\n",
    "    encoder_inputs, encoder_inputs_mask = tokenizer.encode(\n",
    "        src_words, padding_first=False, add_bos=True, add_eos=True, return_mask=True\n",
    "        )\n",
    "\n",
    "    # [BOS] trg [PAD]\n",
    "    decoder_inputs = tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=True, add_eos=False, return_mask=False,\n",
    "        )\n",
    "\n",
    "    # trg [EOS] [PAD]\n",
    "    decoder_labels, decoder_labels_mask = tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=False, add_eos=True, return_mask=True\n",
    "        )\n",
    "\n",
    "    return {\n",
    "        \"encoder_inputs\": encoder_inputs.to(device=device),\n",
    "        \"encoder_inputs_mask\": encoder_inputs_mask.to(device=device),\n",
    "        \"decoder_inputs\": decoder_inputs.to(device=device),\n",
    "        \"decoder_labels\": decoder_labels.to(device=device),\n",
    "        \"decoder_labels_mask\": decoder_labels_mask.to(device=device),\n",
    "    }\n"
   ],
   "outputs": [],
   "execution_count": 41
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "5p79gPo5N4yC",
    "outputId": "2b1d43ae-a2ed-4333-8274-50c62c2fc6c9",
    "ExecuteTime": {
     "end_time": "2025-02-06T04:29:10.548490Z",
     "start_time": "2025-02-06T04:29:10.399668Z"
    }
   },
   "source": [
    "from functools import partial # 固定collate_fct的tokenizer参数\n",
    "\n",
    "#可以调大batch_size,来看最终的bleu，如果GPU内存不够，可以减小batch_size\n",
    "sampler = TransformerBatchSampler(train_ds, batch_size=256, shuffle_batch=True)\n",
    "# https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader\n",
    "sample_dl = DataLoader(train_ds, batch_sampler=sampler, collate_fn=partial(collate_fct, tokenizer=tokenizer)) #partial函数，固定collate_fct的tokenizer参数\n",
    "\n",
    "for batch in sample_dl:#外层是拿每个batch\n",
    "    for key, value in batch.items():#内层是拿每个batch里面是一个字典\n",
    "        print(key)\n",
    "        print(value)\n",
    "    break"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "encoder_inputs\n",
      "tensor([[    1,     7,    17,     6,  4636,  3116,    31, 13407,     4,     3,\n",
      "             0,     0,     0],\n",
      "        [    1,  1310,    12,  2275,  6752,  1029,  8834, 13522,     4,     3,\n",
      "             0,     0,     0],\n",
      "        [    1,    84,    58,   172,   288,     9,    29,    98,    12,    21,\n",
      "            52,     4,     3],\n",
      "        [    1,    17,     6,   488,   442,    12,   499,   199,    73,    11,\n",
      "           235,     4,     3]])\n",
      "encoder_inputs_mask\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])\n",
      "decoder_inputs\n",
      "tensor([[   1,    5,  324,   36, 2123,  672,   18, 2497,    5, 3739, 2933,    4,\n",
      "            0,    0],\n",
      "        [   1,    5, 1270,   19, 2409,   30, 2698,  307,  136, 1213, 1174,    4,\n",
      "            0,    0],\n",
      "        [   1,   87,   51,   30,  242,    9,   28,   53,   19,   83,   62,    4,\n",
      "            0,    0],\n",
      "        [   1,    5,   16,   36,    5,   45,  362,   19,   55,   80,  265,    5,\n",
      "          219,    4]])\n",
      "decoder_labels\n",
      "tensor([[   5,  324,   36, 2123,  672,   18, 2497,    5, 3739, 2933,    4,    3,\n",
      "            0,    0],\n",
      "        [   5, 1270,   19, 2409,   30, 2698,  307,  136, 1213, 1174,    4,    3,\n",
      "            0,    0],\n",
      "        [  87,   51,   30,  242,    9,   28,   53,   19,   83,   62,    4,    3,\n",
      "            0,    0],\n",
      "        [   5,   16,   36,    5,   45,  362,   19,   55,   80,  265,    5,  219,\n",
      "            4,    3]])\n",
      "decoder_labels_mask\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])\n"
     ]
    }
   ],
   "execution_count": 47
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0AadtoM3N4yC"
   },
   "source": [
    "## 定义模型\n",
    "\n",
    "- Transformer模型由Embedding、Transformer-Block组成\n",
    "- Embedding包括：\n",
    "    - WordEmbedding\n",
    "    - PositionEmbedding\n",
    "- Transformer-Block包括：\n",
    "    - Self-Attention\n",
    "    - Cross-Attention\n",
    "    - MLP"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "VTl-sSJmN4yD"
   },
   "source": [
    "### Embedding"
   ]
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T04:37:46.656368Z",
     "start_time": "2025-02-06T04:37:46.649839Z"
    }
   },
   "cell_type": "code",
   "source": "torch.arange(0, 128).unsqueeze(1).shape",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([128, 1])"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 51
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 455
    },
    "id": "y66CxrsBN4yD",
    "outputId": "c703025c-afc5-4012-d8e3-0e9d4ad253ec",
    "ExecuteTime": {
     "end_time": "2025-02-06T05:00:56.938219Z",
     "start_time": "2025-02-06T05:00:56.683058Z"
    }
   },
   "source": [
    "class TransformerEmbedding(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.vocab_size = config[\"vocab_size\"]\n",
    "        self.hidden_size = config[\"d_model\"] # 词向量维度\n",
    "        self.pad_idx = config[\"pad_idx\"]\n",
    "        dropout_rate = config[\"dropout\"]\n",
    "        self.max_length = config[\"max_length\"]\n",
    "\n",
    "        # layers,设置padding_idx可以让pad的词向量全为0\n",
    "        self.word_embedding = nn.Embedding(\n",
    "            self.vocab_size, self.hidden_size, padding_idx=self.pad_idx\n",
    "        )\n",
    "        self.pos_embedding = nn.Embedding(\n",
    "            self.max_length,\n",
    "            self.hidden_size,\n",
    "            _weight=self.get_positional_encoding(\n",
    "                self.max_length, self.hidden_size\n",
    "            ),# 位置编码，权重通过get_positional_encoding函数计算得到\n",
    "        )\n",
    "        self.pos_embedding.weight.requires_grad_(False) # 不更新位置编码的权重\n",
    "        self.dropout = nn.Dropout(dropout_rate) # 随机失活层\n",
    "\n",
    "    def get_word_embedding_weights(self):\n",
    "        return self.word_embedding.weight\n",
    "\n",
    "    # 计算位置信息\n",
    "    @classmethod\n",
    "    def get_positional_encoding(self, max_length, hidden_size):#max_length是最大长度，hidden_size是embedding维度相等\n",
    "        # Compute the positional encodings once in log space.\n",
    "        pe = torch.zeros(max_length, hidden_size) # 初始化位置编码\n",
    "        # .unsqueeze(1) 是将这个一维张量转换为二维张量，即将其形状从 (max_length,) 变为 (max_length, 1)。这个操作在张量的维度上增加了一个维度，使其从一维变为二维，第二维的大小为 1。\n",
    "        position = torch.arange(0, max_length).unsqueeze(1) # 位置信息,从0到max_length-1\n",
    "        div_term = torch.exp(\n",
    "            torch.arange(0, hidden_size, 2)\n",
    "            * -(torch.log(torch.Tensor([10000.0])) / hidden_size)\n",
    "        )# 计算位置编码的权重,为了性能考量（是数学上的对数函数分解）指数对数变换\n",
    "        pe[:, 0::2] = torch.sin(position * div_term)  # 偶数位置 是在列的维度进行填充的\n",
    "        pe[:, 1::2] = torch.cos(position * div_term)  # 奇数位置\n",
    "        return pe\n",
    "\n",
    "    def forward(self, input_ids):\n",
    "        # input_ids: [batch_size, seq_len]\n",
    "        seq_len = input_ids.shape[1]\n",
    "        assert (\n",
    "            seq_len <= self.max_length\n",
    "        ), f\"input sequence length should no more than {self.max_length} but got {seq_len}\"\n",
    "\n",
    "        position_ids = torch.arange(seq_len, dtype=torch.long, device=input_ids.device)\n",
    "        position_ids = position_ids.unsqueeze(0).expand_as(input_ids)\n",
    "        # print(position_ids) #为了调试\n",
    "        # embedding\n",
    "        word_embeds = self.word_embedding(input_ids) # 词嵌入\n",
    "        pos_embeds = self.pos_embedding(position_ids) # 位置编码\n",
    "        embeds = word_embeds + pos_embeds\n",
    "        embeds = self.dropout(embeds)\n",
    "\n",
    "        return embeds\n",
    "\n",
    "\n",
    "def plot_position_embedding(position_embedding):# 绘制位置编码\n",
    "    plt.pcolormesh(position_embedding) # 绘制位置编码矩阵\n",
    "    plt.xlabel('Depth')\n",
    "    plt.ylabel('Position')\n",
    "    plt.colorbar() # 颜色条，-1到1的颜色范围\n",
    "    plt.show()\n",
    "\n",
    "position_embedding = TransformerEmbedding.get_positional_encoding(64, 128)\n",
    "plot_position_embedding(position_embedding)\n"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ],
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 65
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T04:41:25.027107Z",
     "start_time": "2025-02-06T04:41:25.021671Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import math\n",
    "#为了理解指数对数变换\n",
    "def positional_encoding_weights(emb_size):\n",
    "    # 计算底数和指数\n",
    "    base = math.log(10000)\n",
    "    exponent = -5 / emb_size\n",
    "    \n",
    "    # 应用指数函数\n",
    "    weight = math.exp(exponent*base) \n",
    "    \n",
    "    return weight\n",
    "\n",
    "emb_size = 10  # 举例，可以替换成你需要的大小\n",
    "weight = positional_encoding_weights(emb_size)\n",
    "print(weight)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.009999999999999995\n"
     ]
    }
   ],
   "execution_count": 52
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T04:41:38.766885Z",
     "start_time": "2025-02-06T04:41:38.761114Z"
    }
   },
   "cell_type": "code",
   "source": "1/10000**(1/2)",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.01"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 53
  },
  {
   "cell_type": "code",
   "source": [
    "#随机input，调用TransformerEmbedding\n",
    "config={\n",
    "    \"vocab_size\": 100,\n",
    "    \"d_model\": 128,\n",
    "    \"pad_idx\": 0,\n",
    "    \"max_length\": 64,\n",
    "    \"dropout\": 0.1,\n",
    "}\n",
    "input_ids = torch.randint(0, 100, (1, 50))\n",
    "embeds = TransformerEmbedding(config)(input_ids)\n",
    "embeds.shape"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T05:01:00.085316Z",
     "start_time": "2025-02-06T05:01:00.076161Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,\n",
      "         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,\n",
      "         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "torch.Size([1, 50, 128])"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 66
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "wXavO0SNN4yD"
   },
   "source": [
    "### Transformer Block"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "HZL4mu3pN4yD"
   },
   "source": [
    "#### scaled-dot-product-attention"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 970
    },
    "id": "U9QwdVQYN4yD",
    "outputId": "b6ae9ac8-c71c-4a7b-fd4b-9c15e92dba2c",
    "ExecuteTime": {
     "end_time": "2025-02-06T05:54:40.130459Z",
     "start_time": "2025-02-06T05:54:40.114921Z"
    }
   },
   "source": [
    "from dataclasses import dataclass\n",
    "from typing import Optional, Tuple\n",
    "\n",
    "Tensor = torch.Tensor\n",
    "\n",
    "@dataclass  # 为了快速地实例化对象，不用写__init__\n",
    "class AttentionOutput:\n",
    "    hidden_states: Tensor # 我们想要的结果\n",
    "    attn_scores: Tensor  # 注意力分数，用来画热力图\n",
    "\n",
    "class MultiHeadAttention(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"] # 隐藏层大小\n",
    "        self.num_heads = config[\"num_heads\"] # 多头注意力的头数\n",
    "        assert (\n",
    "            self.hidden_size % self.num_heads == 0\n",
    "        ), \"Hidden size must be divisible by num_heads but got {} and {}\".format(\n",
    "            self.hidden_size, self.num_heads\n",
    "        )\n",
    "        self.head_dim = self.hidden_size // self.num_heads # 每个头的维度\n",
    "\n",
    "        # layers\n",
    "        self.Wq = nn.Linear(self.hidden_size, self.hidden_size, bias=False) #第二个self.hidden_size可以*系数\n",
    "        self.Wk = nn.Linear(self.hidden_size, self.hidden_size, bias=False)\n",
    "        self.Wv = nn.Linear(self.hidden_size, self.hidden_size, bias=False)\n",
    "        self.Wo = nn.Linear(self.hidden_size, self.hidden_size, bias=False) # 输出层\n",
    "\n",
    "    def _split_heads(self, x: Tensor) -> Tensor:\n",
    "        \"\"\"\n",
    "        分头\n",
    "        \"\"\"\n",
    "        bs, seq_len, _ = x.shape #假设输入的维度是[batch_size, seq_len, hidden_size],hidden_size是512\n",
    "        x = x.view(bs, seq_len, self.num_heads, self.head_dim) #num_heads是8，head_dim是64\n",
    "        return x.permute(0, 2, 1, 3) #变换维度，[batch_size, num_heads, seq_len, head_dim] 因为最后两维是seq_len和head_dim才能计算缩放点积注意力，所以要交换维度\n",
    "\n",
    "    def _merge_heads(self, x: Tensor) -> Tensor:#将多头注意力的输出合并为一个张量\n",
    "        \"\"\"\n",
    "        合并头\n",
    "        \"\"\"\n",
    "        bs, _, seq_len, _ = x.shape #假设输入的维度是[batch_size, num_heads, seq_len, head_dim]\n",
    "        return x.permute(0, 2, 1, 3).reshape(bs, seq_len, self.hidden_size) # 变换维度，变为[batch_size, seq_len, hidden_size]\n",
    "\n",
    "    def forward(self, querys, keys, values, attn_mask=None) -> AttentionOutput:\n",
    "        # split heads\n",
    "        querys = self._split_heads(self.Wq(querys)) #(batch_size, seq_len,hidden_dim)-->[batch_size, num_heads, seq_len, head_dim] 用一个大的张量存放分好的张量，即(batch_size,seq_len,512)变成(batch_size,8,seq_len,64)\n",
    "        keys = self._split_heads(self.Wk(keys))#[batch_size, num_heads, seq_len, head_dim]\n",
    "        values = self._split_heads(self.Wv(values))#[batch_size, num_heads, seq_len, head_dim]\n",
    "\n",
    "        # calculate attention scores\n",
    "        qk_logits = torch.matmul(querys, keys.mT) # 计算注意力分数，matmul是矩阵乘法，mT是矩阵转置,qk_logits是[batch_size, num_heads, seq_len, seq_len] torch的矩阵相乘只会那它最后两维\n",
    "        # print(querys.shape[-2], keys.shape[-2])  #3 4\n",
    "        if attn_mask is not None:\n",
    "            attn_mask = attn_mask[:, :, : querys.shape[-2], : keys.shape[-2]]  # 后面两个都是seq_len，可以不相等\n",
    "            qk_logits += attn_mask * -1e9 # 给需要mask的地方设置一个负无穷  把padding部分去掉\n",
    "        attn_scores = F.softmax(qk_logits / (self.head_dim**0.5), dim=-1) # 计算注意力分数 缩放\n",
    "\n",
    "        # embeds的尺寸是[batch_size, num_heads, seq_len, head_dim]\n",
    "        embeds = torch.matmul(attn_scores, values) # softmax后的结果与value相乘，得到新的表示\n",
    "        embeds = self.Wo(self._merge_heads(embeds)) # 输出层 [batch_size, seq_len, hidden_size]\n",
    "\n",
    "        return AttentionOutput(hidden_states=embeds, attn_scores=attn_scores)\n",
    "\n",
    "mha = MultiHeadAttention({\"num_heads\": 2, \"d_model\": 2})\n",
    "query = torch.randn(2, 3, 2) # [batch_size, seq_len, hidden_size]\n",
    "query /= query.norm(dim=-1, keepdim=True) # 归一化\n",
    "key_value = torch.randn(2, 4, 2)\n",
    "print(f'key_value.shape {key_value.shape}')\n",
    "outputs = mha(query, key_value, key_value) #最终输出shape和query的shape一样\n",
    "print(outputs.hidden_states.shape)\n",
    "print(outputs.attn_scores.shape)  # 两个样本，每个样本有两个头，每个头都算出来一个三行四列的矩阵"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "key_value.shape torch.Size([2, 4, 2])\n",
      "torch.Size([2, 3, 2])\n",
      "torch.Size([2, 2, 3, 4])\n"
     ]
    }
   ],
   "execution_count": 72
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-06T05:30:35.199617Z",
     "start_time": "2025-02-06T05:30:34.953915Z"
    }
   },
   "cell_type": "code",
   "source": [
    "#随机一个2阶张量，在-1维度计算softmax\n",
    "import torch.nn.functional as F\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "x = torch.randn(2, 3)\n",
    "x_softmax = F.softmax(x, dim=-1)\n",
    "print(x_softmax)\n",
    "\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[0.7653, 0.1038, 0.1308],\n",
      "        [0.0864, 0.4972, 0.4163]])\n"
     ]
    }
   ],
   "execution_count": 67
  },
  {
   "cell_type": "code",
   "source": [
    "# plt.subplots() 用于创建子图网格，其维度基于 outputs.attn_scores.shape[:2]。子图的行数和列数由 outputs.attn_scores 的前两个维度确定。\n",
    "fig, axis = plt.subplots(*outputs.attn_scores.shape[:2])\n",
    "for i in range(query.shape[0]):\n",
    "    for j in range(outputs.attn_scores.shape[1]):\n",
    "        # axis[i, j].matshow(outputs.attn_scores[i, j].detach().numpy())：此行使用 Matplotlib 的 matshow 绘制每个 i 和 j 的注意力分数热图。detach().numpy() 将 PyTorch 张量转换为 NumPy 数组以进行可视化。\n",
    "        axis[i, j].matshow(outputs.attn_scores[i, j].detach().numpy())\n",
    "        for x in range(outputs.attn_scores.shape[2]):\n",
    "            for y in range(outputs.attn_scores.shape[3]):\n",
    "                # axis[i, j].text(y, x, f\"{outputs.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")：此代码在热图上叠加文本，显示 (x, y) 位置处的注意力分数。格式化部分 f\"{outputs.attn_scores[i, j, x, y]:.2f}\" 确保以两位小数显示注意力分数。文本以白色居中显示在 (y, x) 坐标处。\n",
    "                axis[i, j].text(y, x, f\"{outputs.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")\n",
    "fig.suptitle(\"multi head attention without mask\")\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T05:57:48.757658Z",
     "start_time": "2025-02-06T05:57:48.288622Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 4 Axes>"
      ],
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 73
  },
  {
   "cell_type": "code",
   "source": [
    "print('-'*50)\n",
    "# mask\n",
    "mask = torch.Tensor([[0, 0, 1, 1], [0, 0, 0, 1], [0, 0, 0, 0]]).reshape(1, 1, 3, 4) #手工构造mask\n",
    "outputs_masked = mha(query, key_value, key_value, mask)\n",
    "\n",
    "fig, axis = plt.subplots(*outputs_masked.attn_scores.shape[:2])\n",
    "for i in range(query.shape[0]):\n",
    "    for j in range(outputs_masked.attn_scores.shape[1]):\n",
    "        axis[i, j].matshow(outputs_masked.attn_scores[i, j].detach().numpy())\n",
    "        for x in range(outputs_masked.attn_scores.shape[2]):\n",
    "            for y in range(outputs_masked.attn_scores.shape[3]):\n",
    "                axis[i, j].text(y, x, f\"{outputs_masked.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")\n",
    "fig.suptitle(\"multi head attention with mask\")\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T06:00:45.983276Z",
     "start_time": "2025-02-06T06:00:45.641050Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 4 Axes>"
      ],
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhUAAAG6CAYAAAC/RrTYAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAc3BJREFUeJzt3Qd4FEX/B/DvpfcOCQFC6L2G3gWkI10USwCxgr6Iin8QC74idkHBioCNV0QIIEhvCoReAySQ0NJ7D+n3f2aSXHLhAgksd8n6/TzPEXZvbm9/N3tzv52dudNotVotiIiIiO6R2b1ugIiIiEhgUkFERESKYFJBREREimBSQURERIpgUkFERESKYFJBREREimBSQURERIpgUkFERESKYFJBREREimBSQdXSlClT4OvrW6my77zzDjQazR3L9e/fH23atEF1ImIUsf5bVKVeq4PKHltlyyYkJEAN9eTg4GDq3aAaiEkF1QhZWVmy0d63b5+pd6Vae//997Fhw4Zb1h86dEi+fikpKfd9H6KiouRznT59Gv+m15iImFRQDUoqFixYYDCpmD9/Pm7evGmS/apJSYV4/YyVVIjnMpRUfP/99wgJCUFNYejYYlJBVDGL29xHVCNYWFjIG1V/lpaWqEl4bBFVDXsqqNJKrhlfunQJjz/+OJydnVGrVi28+eabED92Gx4ejtGjR8PJyQleXl749NNP9R6/atUq+fhr167prRe9D2J9RZc2RHnxPII4AxZlxU3sT9n9qqwLFy7ggQcegJ2dHerWrYuPPvroljI5OTl4++230aRJE1hbW6N+/fqYM2eOXF/WypUrMWDAANSuXVuWa9WqFb7++utbtiden/feew/16tWTzyue//z585Xe508++QQ9e/aEu7s7bG1t4efnhz/++EOvjHgNMjMz8eOPP+peI3FtXLw+r732mizTsGFD3X1l6+GXX36R2xTbdnNzwyOPPCLr09CYlNu9fqIOu3TpIv8/depU3XOJuq9oTIXY51deeUW+xuI1bN68uYy3/A8oi+3MnDlT9hKI/RBlW7dujW3btt32tRPb8fDwwOzZs3XrCgsL4eLiAnNzc73emw8//FAmERkZGQaPrYpe47LE9sQ6sX3xHhGvg+hpu5OS1/fs2bPo16+ffH3F8VdSz/v370e3bt1kHYnXaNeuXXqPv379Ol544QV5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CCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgLmEhyqCvMbGygShqom9rjEyF+WhtqVJidDczbiJoq55qDatsN6yR1n+NltMiG2kXM7Qk1KsjJBj6tXLuh7qOYiIiIjIZJBRERESmCSQUREREpgkkFERERKYJJBRERESmCSQUREREpgkkFERERKYJJBRERESmCSQUREREpgkkFERERKYJJBRERESmCSQUREREpgkkFERERKYJJBRERESmCSQUREREpgkkFERERKYJJBRERESmCSQUREREpgkkFERERKYJJBRERESmCSQUREREpgkkFERERKcICNcwT7Trgmc6dUcvOHhcT4vHO3j04ExtjsOwjbdpiXMtWaObuIZfPxcXik4MH9Mp/PHgIJrRqo/e4/deuYsqG9TBZfH5l4tt3m/haG4jvULn4Hqwgvo0mjK9Tmfj23yG+FuXiCywX3yAD8V03YXztO+BpUX/29rgYX3R8nq0gvkni+GxVGl9QXCw+PnBAr/xH4vhsfWv9TQ0wTXw1maybzuXqJqaCumlb/N7yKK6b2Fh8fPDALeUbu7nh9T590a1ePZibmSE0MREv/LkJUenpMLbJ3dpjWm8/eDjYIzgmHgs378W5yFiDZSd2boOHOrRCU093uXwhKg6f7zigK29hZob/DOqJvs0aop6bMzKycxAYdgOf7jiA+PRMmMITzTrh2VbdUMvWAReT4/D2sR04kxhtsOyQ+s0wo01P+Dq6yliupSXj+4tHEXA1SK/MY007oa27F1ytbTF8yw+4kBwHU5ks6q9PufqLuE39dSxTf5Fx+HznAV15WX8PGqi/7fe//qqUVCxatAjr169HcHAwbG1t0bNnT3z44Ydo3rw5jGFEs+Z4o28/zN+zC6djojGtox9+HDseA39cgcSbN28p361efWwKCcaJ6Cjk5Bfguc5d8NO48Rj804+IzczQldt37Spe27FNt5xbUABTGNG0Od7o0w/z9xbH18EPP44Zj4E/3Sa+S8E4ERWFnILi+MaOx+CfDcS3sxrFJ+ovtji+0eMx8OcK4qtbHF90cXx+XfDTmPEY/IuB+HZVg/iaNce8vv3w5u6i+pvayQ8/jhuPQasMx9e9Xn38GVzm+OxSdHwOKX98Xr2KOdXg+LwX1aHtmNevuG6iy9TNytvUjWg79hqom4yiuvFxdsbvkx7B70FBWHzoEDJyc9DU3QM5+fkwtmFtmuH1YX3xzqbdOBsegyd7dsL3U8Zh+OJVSMq8Nb4uDevhr7PBOHUjWu7v9L5dsHzKOIz64ifEpWfCxtICrbxr4+t9R+QHnLONNeaO6I+vHh+NiV+vNnp8Ixu0xHy/gZh/ZBtOJUZhWosu+GnAJAzY9B0Sc7JuKZ+am41lQYcQmpqIvMICDKzbBB/3GIHE7Ez8HX1VlrGzsMLx+HBsuXERH3YfDlMa1rYZXh/eF+9sLK6/XsX193kl6i+vXP2llam/vcX1Z1tcf0+MxsSvVlefyx/79+/HjBkzcPjwYezcuRN5eXkYPHgwMjONk7lO7+SHNUHn8MeF8whNSsIbu3fiZn4eJrZua7D8y9v+wi9nz8izkivJSfi/XTuggQa9fHz0yolGOiErS3dLy8mBKcj4zpeJb88d4tteHF9CufjqV9P4OhbX38Vy8bWqIL4df+GXc2Xi270DGk31je+pcsfn/F3F8bWp5PG5syi+ntX0+LwXpm47nvIrrpvzlaybrX/hlzMG6qbMsfdKr94y4fvwn79xIT4ON1JTsftKmMEk5X7z79UJa48HIeDkBYTFJ+GdTbuQnZePcX76vVwl5qzdhv8dPSs/cK4mJOPNgJ0w02jQo3FRfBk5uXhq1XpsC7qEawnJOBMRg/c270Wbup6o4+xo5OiA6S274rfQM1h75ZxMFN44sg03C/LxcJN2Bssfjr2B7eGXEJaWiBsZKVgZchzBKXHoXLu+rozotfji3EEcjL4GU/MvX38bK1F/R84iOLpc/TUqU38ry9RfeAze+9M49Velnopt20rPloRVq1ahdu3aOHHiBPr27Yv7ydLMDG1qe+KrY0d167QADt64gU516lRqG7YWFrA0N0NKdrbe+u716uHYM88jLTsbhyJu4NNDB28pc7/dNj6vKsaXYyC+p59HWk42DoXfwKeBJozveLn4wqtYf2YV1N/0MvEdNlF8np742kD9dazi8ZlqIL6jzxYdn4Hhpjk+75XJ2w5RN0fL1c31u68bDYAHGjXCd8eOYdW48WhVuzYiUlPlc+wMC4Uxif1q7e2J7/8+plun1UJ2d3eoX7n4xJmthbk5Um9WfFw52lijsFCLtGzjJrWy/ty88FXQIf36i76GTh51K7WNnl4N0MjJDR+c3IvqxrKk/vaXq7/QG+jgU/Pq757GVKSmpsq/bm5uFZbJycmRtxJpaWl39VyutrbyOlFClv6ZjThzE9c1K+P13n0Rm5GJAzeu69btv3YN20NDEZ6aCh8XF7zWszdWjRmHcWv+h0JRs0Zy3+K7XhxfWip8nIvjGz0O436vRvG5VjK+Xn0Rm5mJA+Hl4gsrF99D4zBubQ2Mr8+t9fd38fEZUXx8vtqrN1aOHYfxvxk3PqXdqe1Qqt1Q7L1Vrm7c7ezgYGWF57p2xWcHD8jein6+DfH1Qw9h8trfcTQiAsbiYmcLC3MzJGboXwYQyw09XCu1jVeH9EFcegYOhd0weL+VhTleGdwbW84FIzMnF8bkam1XVH/Z+vHFZ2eisXPRmAJDHC2tcXjcTFiZm8v3yvyj23EgxvS9ElWqv1qVrL+hfRCXdof6G9IbW87e//q766SisLAQs2bNQq9evdCmjeEumpJrqQsWLICpPde5K0Y1b45H//hd75r05kshuv+HJCYgOD4ef0+bLq+pirPemkLG16w5Hl13h/gS4vH31BoYn18F8V02EN+U6ehet77sdaopnuvSFSObN5cfSHeqv/018PisattRXdoNXd20aI7Jv5fWjehqFnaFhWLFyZPy/+JSSSdvbzzWrr1Rk4p7Ja7HD2vbHP4/rEVu/q3jdcQH+ueTRkCEvGDTHtQUGXk5GL5lBewtLdHTyxdv+g1EeEaKvDSiJtNL6m/5bervEePV311PKRXXR4OCgvDbb7/dttzcuXPlWUnJLTw8/K6eL/nmTeQXFsLDzl5vvYedHeLvcF326U6d8XyXLnhy/ToEJyTctqw4403MykIDFxcY0z3H17kLngyoQnzO1Si+cmeQ5T3dsTi+DesQnFiJ+G5Ws/q7Q3zT/TrLQbb+lTk+U01zfCqpMm2HUu3Gvb63ZN106QL/dfp1I7aZV1CAy4mJeuXDkhJRx9G4Yw5Ssm4iv6AQ7g52euvFckK5s9/ypvbyw9N9OmP6qvW4FJtQ4QeSt4uTvEZv7F4KITknq6j+bPTjq2Vjj/ibpQOayxP9eNczkuWMjuUXj+KvG8F4oXUPVDcp91J/vf3wdN871N+jxfW3wjj1d1dJxcyZM7F582bs3bsX9erVu21Za2trODk56d3uRl5hoZxyV3aQnjhXEAOnTkYbnlYkPOvXBTO7dYd/wHo5JfFOvBwcZHfpnRobpd02vpg7xNe1O/w31OD4bld/nYrj21iF+GxMFF9srN5AvpL4Tt0mvmc6d8GL3bpjijg+Yytff3FGjk8plW07lGo39OrGp1zd+FSibrobrhuxzbOxsWhU7tKWr6srotLv/lLN3cgrKMT5qFh0b1Q6CFGclYrl0+EVx/dU7854/oFueObHAPn4ihKKBu4umLZyHVJuc73+fpL1lxQjexv06s+rAU4mRFZ6O2bQyEsh1U1eSf01Lld/jevj9I3b1F+fMvVnYOpwSUIh62+F8eqvSpc/tFotXnzxRQQEBGDfvn1o2LAhjGn5yRP4dPBQOY//TEwMpnXqBDtLS/xxoWjusbgvJjNDzicXnu3cBS9374lZ2/5CRFqqPDMRsvLy5E089j/demBr6GV5NinO3v+vd19cT0nG39eNf+1NF19ccXwdDcSXkYGPDxXH51cc3/ZKxJeZKc9u/69XcXw3TBDfqRP49MHi+ouNwbQOnWBnUSa+B4vrr3x8t6u/rj2wNaw4vrL1Z4L4fjh5Ap8MGYpzxfU3taT+zhfFJ+4T0xHLHp+zevSUMw0qiu+l7j2w7XLp8Smu7Yv4/jHB8XkvTN12/HDiBD4ZOhTnituOqZ3K1c3Q4ro5UFw3XcrUTeqtdSN8f/wYvhgxEkcjI3A4PBx9fX0xsFFjeZnE2H48eBKLxg9BUFQczkWIKaUdYWtliYAT5+X9H4wfgti0DHy+86Bcnt6nM14c2AOv/r4VkSlp8Cg+S87KzZM38YG0+NGRclri8z9vgLmZRldGDAYUH4TGJHoaPu05EueSYnA6IQpPtewi2461YWfl/eK+2Kx0fHR6v1wWPRJnE6NxPSMFVmbmeKBuY4xt1EaOqyjhbGWDuvZOqG1b1LPUyKlofEb8zUw5XsMk9RdZQf1NKK6/HWXqb1Bx/SVXUH+TR6JVHePXn0VVuy1Xr16NjRs3wtHRETHFXwTj7Ows557fb1suhcDd1haze/SSb3Ix1XDKhnVywJXg7eSEQtnpVeTxdu1hbWGBr0c+pLedxYcPYcnhQBQUatGiVi2Ma9UaTtbWiMvMwD/Xr+OzwIMm+S6ALZeL4+teQXyOTnqD83TxjTAQ35Hi+DxqYVzLcvEdrgbx2dvJa9BTNq5Dws0K4mvbHtbmBuI7cpv4bpiw/i6FwM3WFi+XHJ8ivoCK6++x4vr7apR+fEsCyxyfHmWOz4yi+D4/ZJr47kV1aDvc7Gzxcs8ydbP+LusmMFD+f0doKN7ctQvPd+2Ktx94AFeSkuUXXx2PqvzZs1K2Bl2Cq70tXhrYQ354XIyOl2ewiZlF8dVxcdSL75Gu7WBlYYEvJo/S287SPYFYtucwajs5YGDLxnLdhplP6JV58oe1OHbVuGNGNl+/CDdrO7zcrg9q2drLL7/y3/O7bvCmSA5E4lrC1sIS/+06BHXsHJFdkC+nlr588E+5nRIP1muKT3qO1C0v7TNG/l189h8sPluUXBrL1nNl6s+xuP5Wlak/53L1162C+tttoP5eLFd/y+9v/Wm0ZWviToWLByeVt3LlSkyZMqVS2xCjuEVD4rPoPZjZ2ECVDL9M6qH2+ESINeszu9IKs7Nxfd58OU7hXi4pVNW9th0l7UaDheptN6yT1P2rCTdb1Kxp0HfD9qI6j82CnGxc/nRepdqNKl/+ICKqKrYdRP8O6k6NiYiIyGiYVBAREZEimFQQERGRIphUEBERkSKYVBAREZEimFQQERGRIphUEBERkSKYVBAREZEimFQQERGRIphUEBERkSKYVBAREZEimFQQERGRIphUEBERkSKYVBAREZEimFQQERGRIphUEBERkSKYVBAREZEimFQQERGRIphUEBERkSKYVBAREZEiLGBkWq1W/i3MzoZqaaBuao9PhFgAVSp535W8D2uKf0O7UZCj7nO8wpvqrbsSBTlQpYKcyrcbGq2RW5eIiAjUr1/fmE9JROWEh4ejXr16qCnYbhDVjHbD6ElFYWEhoqKi4OjoCI3m/p/ypqWlycZIvBhOTk5QG8ZXsxk7PvF2T09Ph7e3N8zMas6ZMdsNZTG+mi2tGrcbRr/8IXbIFGdI4oVX48FVgvHVbMaMz9nZGTUN2437g/HVbE7VsN2oOacqREREVK0xqSAiIiJFqD6psLa2xttvvy3/qhHjq9nUHl9NpfZ6YXw1m3U1js/oAzWJiIhInVTfU0FERETGwaSCiIiIFMGkgoiIiBTBpIKIiIgUwaSCiIiIFMGkgoiIiBTBpIKIiIgUwaSCiIiIoIT/ByTzjNYVNsu/AAAAAElFTkSuQmCC"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 75
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "GkzzrRWVN4yE"
   },
   "source": [
    "#### Transformer-Block"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "b16xpL47N4yE",
    "ExecuteTime": {
     "end_time": "2025-02-06T06:13:00.406273Z",
     "start_time": "2025-02-06T06:13:00.391157Z"
    }
   },
   "source": [
    "# 通过使用 @dataclass 装饰器，Python 会自动为该类生成一些方法，如 __init__()、__repr__() 和 __eq__() 等，这些方法可以使类的使用更加方便。\n",
    "@dataclass\n",
    "class TransformerBlockOutput:\n",
    "# hidden_states: Tensor：用于存储某个块产生的隐藏状态。\n",
    "# self_attn_scores: Tensor：包含了自注意力机制（self-attention）所计算得到的注意力分数。\n",
    "# cross_attn_scores: Optional[Tensor] = None：是一个可选字段，存储了交叉注意力（cross-attention）计算得到的注意力分数。这里的 Optional 表示这个字段可以是 Tensor 类型，也可以是 None。\n",
    "    hidden_states: Tensor\n",
    "    self_attn_scores: Tensor  # 自注意力分数\n",
    "    cross_attn_scores: Optional[Tensor] = None  # 交叉注意力分数\n",
    "\n",
    "class TransformerBlock(nn.Module):\n",
    "    def __init__(self, config, add_cross_attention=False): # encoder的add_cross_attention=False,decoder的add_cross_attention=True\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"]\n",
    "        self.num_heads = config[\"num_heads\"]\n",
    "        dropout_rate = config[\"dropout\"]\n",
    "        ffn_dim = config[\"dim_feedforward\"] # 前馈神经网络的维度\n",
    "        eps = config[\"layer_norm_eps\"] # 层归一化的epsilon值\n",
    "\n",
    "        # self-attention\n",
    "        self.self_atten = MultiHeadAttention(config) # 多头注意力\n",
    "        self.self_ln = nn.LayerNorm(self.hidden_size, eps=eps) #层归一化(层标准化)\n",
    "        self.self_dropout = nn.Dropout(dropout_rate)\n",
    "\n",
    "        # cross-attention，交叉注意力，decoder中使用,因此额外做一个判断\n",
    "        if add_cross_attention:\n",
    "            self.cross_atten = MultiHeadAttention(config)\n",
    "            self.cross_ln = nn.LayerNorm(self.hidden_size, eps=eps)\n",
    "            self.cross_dropout = nn.Dropout(dropout_rate)\n",
    "        else:\n",
    "            self.cross_atten = None\n",
    "\n",
    "        # FFN,前馈神经网络\n",
    "        self.ffn = nn.Sequential(\n",
    "            nn.Linear(self.hidden_size, ffn_dim),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(ffn_dim, self.hidden_size),\n",
    "        )\n",
    "        self.ffn_ln = nn.LayerNorm(self.hidden_size, eps=eps)\n",
    "        self.ffn_dropout = nn.Dropout(dropout_rate)\n",
    "\n",
    "    def forward(\n",
    "        self,\n",
    "        hidden_states,\n",
    "        attn_mask=None,\n",
    "        encoder_outputs=None,\n",
    "        cross_attn_mask=None,\n",
    "    ):\n",
    "        # self-attention,自注意力  这里的三个hidden_states就是x,x,x\n",
    "        self_atten_output = self.self_atten(\n",
    "            hidden_states, hidden_states, hidden_states, attn_mask\n",
    "        )\n",
    "        self_embeds = self.self_ln(\n",
    "            hidden_states + self.self_dropout(self_atten_output.hidden_states)\n",
    "        ) #多头注意力进行dropout，然后和原始输入进行残差连接，然后进行层归一化\n",
    "\n",
    "        # cross-attention，交叉注意力\n",
    "        if self.cross_atten is not None:\n",
    "            assert encoder_outputs is not None\n",
    "            cross_atten_output = self.cross_atten(\n",
    "                self_embeds, encoder_outputs, encoder_outputs, cross_attn_mask\n",
    "            ) #query是self_embeds，key和value都是encoder_outputs\n",
    "            cross_embeds = self.cross_ln(\n",
    "                self_embeds + self.cross_dropout(cross_atten_output.hidden_states)\n",
    "            ) # 交叉注意力进行dropout，然后和self_embeds进行残差连接，然后进行层归一化\n",
    "\n",
    "        # FFN\n",
    "        embeds = cross_embeds if self.cross_atten is not None else self_embeds # 如果有交叉注意力，则使用交叉注意力的输出作为FFN的输入；否则，使用self_embeds作为FFN的输入\n",
    "        ffn_output = self.ffn(embeds) # 前馈神经网络\n",
    "        embeds = self.ffn_ln(embeds + self.ffn_dropout(ffn_output)) # 前馈神经网络进行dropout，然后和原始输入进行残差连接，然后进行层归一化\n",
    "\n",
    "        return TransformerBlockOutput(\n",
    "            hidden_states=embeds,\n",
    "            self_attn_scores=self_atten_output.attn_scores,\n",
    "            cross_attn_scores=cross_atten_output.attn_scores\n",
    "            if self.cross_atten is not None\n",
    "            else None,\n",
    "        )"
   ],
   "outputs": [],
   "execution_count": 76
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "tfJIGaohN4yE"
   },
   "source": [
    "#### Encoder"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "sTLabHm7N4yE",
    "ExecuteTime": {
     "end_time": "2025-02-06T06:18:27.806313Z",
     "start_time": "2025-02-06T06:18:27.796363Z"
    }
   },
   "source": [
    "from typing import List\n",
    "\n",
    "@dataclass\n",
    "class TransformerEncoderOutput:\n",
    "    last_hidden_states: Tensor\n",
    "    attn_scores: List[Tensor]\n",
    "\n",
    "# https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module\n",
    "class TransformerEncoder(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.num_layers = config[\"num_encoder_layers\"]\n",
    "\n",
    "        # layers,仅仅是一个模块的列表，它本身没有定义前向传递（forward pass）过程。你需要在 forward 方法中明确地定义如何使用这些模块。\n",
    "        self.layers = nn.ModuleList(\n",
    "            [TransformerBlock(config) for _ in range(self.num_layers)]\n",
    "        )\n",
    "\n",
    "    def forward(\n",
    "        self, encoder_inputs_embeds, attn_mask=None\n",
    "    ) -> TransformerEncoderOutput:\n",
    "        attn_scores = [] # 存储每个层的注意力分数\n",
    "        embeds = encoder_inputs_embeds # 输入的嵌入向量作为第一层的输入(embedding+位置编码)\n",
    "        for layer in self.layers:\n",
    "            block_outputs = layer(embeds, attn_mask=attn_mask)\n",
    "            embeds = block_outputs.hidden_states #上一层的输出作为下一层的输入\n",
    "            # 在每个层的输出中，提取了隐藏状态 block_outputs.hidden_states，并将对应的注意力分数 block_outputs.self_attn_scores 添加到列表 attn_scores 中。\n",
    "            attn_scores.append(block_outputs.self_attn_scores) # 存储每个层的注意力分数,用于画图\n",
    "\n",
    "        return TransformerEncoderOutput(\n",
    "            last_hidden_states=embeds, attn_scores=attn_scores\n",
    "        )\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 77
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "BEMNj6eBN4yE"
   },
   "source": [
    "#### Decoder"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "wCHOur-QN4yE",
    "ExecuteTime": {
     "end_time": "2025-02-06T07:13:40.855776Z",
     "start_time": "2025-02-06T07:13:40.831415Z"
    }
   },
   "source": [
    "@dataclass\n",
    "class TransformerDecoderOutput:\n",
    "    last_hidden_states: Tensor\n",
    "    self_attn_scores: List[Tensor]\n",
    "    cross_attn_scores: List[Tensor]\n",
    "\n",
    "\n",
    "class TransformerDecoder(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.num_layers = config[\"num_decoder_layers\"]\n",
    "\n",
    "        # layers\n",
    "        self.layers = nn.ModuleList(\n",
    "            [\n",
    "                TransformerBlock(config, add_cross_attention=True)\n",
    "                for _ in range(self.num_layers)\n",
    "            ]\n",
    "        )\n",
    "\n",
    "    def forward(\n",
    "        self,\n",
    "        decoder_inputs_embeds,\n",
    "        encoder_outputs,\n",
    "        attn_mask=None,\n",
    "        cross_attn_mask=None,\n",
    "    ) -> TransformerDecoderOutput:  #->表示返回值类型\n",
    "        self_attn_scores = [] # 存储每个层的自注意力分数\n",
    "        cross_attn_scores = [] # 存储每个层的交叉注意力分数\n",
    "        embeds = decoder_inputs_embeds # 输入的嵌入向量作为第一层的输入(embedding+位置编码)\n",
    "        for layer in self.layers:\n",
    "            block_outputs = layer(\n",
    "                embeds,\n",
    "                attn_mask=attn_mask, # 自注意力的mask\n",
    "                encoder_outputs=encoder_outputs,\n",
    "                cross_attn_mask=cross_attn_mask, # 交叉注意力的mask\n",
    "            )\n",
    "            embeds = block_outputs.hidden_states # 上一层的输出作为下一层的输入\n",
    "            self_attn_scores.append(block_outputs.self_attn_scores) # 存储每个层的自注意力分数，为了画图\n",
    "            cross_attn_scores.append(block_outputs.cross_attn_scores) # 存储每个层的交叉注意力分数\n",
    "\n",
    "        return TransformerDecoderOutput(\n",
    "            last_hidden_states=embeds,\n",
    "            self_attn_scores=self_attn_scores,\n",
    "            cross_attn_scores=cross_attn_scores,\n",
    "        )\n"
   ],
   "outputs": [],
   "execution_count": 78
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zPrvXAXtN4yF"
   },
   "source": [
    "#### mask\n",
    "\n",
    "- mask实际上大类上只有两种\n",
    "    1. `padding_mask`：mask掉`pad_idx`，不计算损失\n",
    "    2. `attention_mask`：mask掉`pad_idx`，不计算注意力分数\n",
    "- Decoder的`attention_mask`和Encoder有一定的区别：\n",
    "    - Encoder可以同时看见序列所有信息，故只mask掉`pad_idx`\n",
    "    - Decoder只能看到在自身之前的序列的信息，故要额外mask掉自身之后的序列"
   ]
  },
  {
   "cell_type": "code",
   "source": "(torch.triu(torch.ones(5, 5)) == 0).transpose(-1,-2)",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "D2N9VmcAWLn1",
    "outputId": "560b8af9-c854-4fd7-df3e-86e9eb9045cc",
    "ExecuteTime": {
     "end_time": "2025-02-06T07:21:02.031382Z",
     "start_time": "2025-02-06T07:21:02.023734Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[False,  True,  True,  True,  True],\n",
       "        [False, False,  True,  True,  True],\n",
       "        [False, False, False,  True,  True],\n",
       "        [False, False, False, False,  True],\n",
       "        [False, False, False, False, False]])"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 82
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 426
    },
    "id": "QxpSYOsaN4yF",
    "outputId": "1022d3c3-c72e-4798-b998-c71c6690b94e",
    "ExecuteTime": {
     "end_time": "2025-02-06T07:13:47.475751Z",
     "start_time": "2025-02-06T07:13:47.172183Z"
    }
   },
   "source": [
    "\n",
    "def generate_square_subsequent_mask(sz: int) -> Tensor:\n",
    "    \"\"\"\n",
    "    Generate a square mask for the sequence. The masked positions are filled with True.\n",
    "        Unmasked positions are filled with False.\n",
    "    \"\"\"\n",
    "    # torch.ones(sz, sz): 创建一个全为 1 的 sz × sz 的矩阵。\n",
    "    # torch.triu(...): 使用 triu 函数取得矩阵的上三角部分，将主对角线以下部分置零。\n",
    "    mask = (torch.triu(torch.ones(sz, sz)) == 0).transpose(-1, -2).bool()\n",
    "    # mask = torch.triu(torch.ones(sz, sz))\n",
    "    return mask\n",
    "\n",
    "\n",
    "plt.matshow(generate_square_subsequent_mask(16)) #画一个16×16的矩阵热力图，黄色的部分维True,是掩码\n",
    "plt.colorbar()\n",
    "plt.xlabel(\"keys\")\n",
    "plt.ylabel(\"querys\")\n",
    "plt.title(\"1 means mask while 0 means unmask\")\n",
    "plt.show()"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 480x480 with 2 Axes>"
      ],
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 80
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 960
    },
    "id": "cPeMjXO1N4yF",
    "outputId": "e6922ce8-ba71-491f-d7a6-709a14a4b7d7",
    "ExecuteTime": {
     "end_time": "2025-02-06T07:29:07.924752Z",
     "start_time": "2025-02-06T07:29:07.077531Z"
    }
   },
   "source": [
    "#通过下面代码查看mask的效果\n",
    "inputs_words = [\"The quick brown fox jumps over the lazy dog .\", \"What does the fox say ?\"]\n",
    "\n",
    "inputs_ids, input_mask = tokenizer.encode([w.split() for w in inputs_words], return_mask=True)\n",
    "for i in range(len(inputs_words)):\n",
    "    decode_text = tokenizer.decode(inputs_ids[i: i+1].tolist(), remove_bos=False, remove_eos=False, remove_pad=False, split=True)[0]\n",
    "    print(decode_text)\n",
    "    # print(input_mask[i].reshape(1, -1))\n",
    "    self_attn_mask  = input_mask[i].reshape(1, -1).repeat_interleave(inputs_ids.shape[-1], dim=0)  # repeat_interleave是把张量重复inputs_ids.shape[-1]次\n",
    "    # print(input_mask[i].reshape(1, -1).repeat_interleave(inputs_ids.shape[-1], dim=0)) \n",
    "    look_ahead_mask = generate_square_subsequent_mask(inputs_ids.shape[-1])\n",
    "\n",
    "    fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n",
    "    axs[0].matshow(self_attn_mask)\n",
    "    axs[0].set_title(\"self_attn_mask\")\n",
    "    axs[0].set_yticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[0].set_ylabel(\"querys\")\n",
    "    axs[0].set_xticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[0].set_xlabel(\"keys\")\n",
    "    axs[1].matshow(look_ahead_mask)\n",
    "    axs[1].set_title(\"look_ahead_mask\")\n",
    "    axs[1].set_yticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[1].set_ylabel(\"querys\")\n",
    "    axs[1].set_xticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[1].set_xlabel(\"keys\")\n",
    "    plt.show()\n",
    "    print('-'*50)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['[BOS]', '[UNK]', 'quick', 'brown', '[UNK]', 'jumps', 'over', 'the', '[UNK]', 'dog', '.', '[EOS]']\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ],
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1IAAAG1CAYAAADz+MUUAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAQQBJREFUeJzt3Qd8FNXa+PEnIRBpCYgIBKJ0pAoGBA0lXJrYQH0pggKCogheVEDJRUVRiYCgr1gQERRBynsFQa9Ik9ARUEJRUboJEQWBJKIEQub/eY7/3ZtGkknZ+vt+PpOdnZ2dMzObnWefM2fOBFiWZQkAAAAAIN8C8z8rAAAAAECRSAEAAACATSRSAAAAAGATiRQAAAAA2EQiBQAAAAA2kUgBAAAAgE0kUgAAAABgE4kUAAAAANhEIgUAAAAANpFIAZfxwQcfSEBAgBw9ejTT9ClTpkjt2rWlRIkS0rx5c7etn697/vnnzf4/deqUu1cFAPIdI4rKoEGDpFy5cuLq7dm5c6d4WhzwVsQx30ciBdiwatUqeeqppyQyMlLmzJkjEydOLNLlb9myxRx4z549m+01LevTTz8t0vIAAABQMCRSgA1fffWVBAYGyvvvvy8DBgyQW2+9tcgTqRdeeIFECgAAwMORSAE2/Pbbb1K6dGkpVaqUu1cFAAAAbkQiBZ+TkpIijz/+uNSsWVOCg4Pl6quvli5dusi3337rnOfrr7+WW265RUJDQ6VMmTLSoUMH2bx5c67L1XbO2pzv3LlzZlwHbVOeH3v27DHt3fXaqiuuuEKqVq0qgwcPlt9//905jzbpGzNmjBmvVauWswxtf6+PWu6HH37onK7Lc7xPnx88eNBMq1ChgtmuBx54QP78888Ctcn/+eef5fbbbzfj1atXl7feesu8vnfvXvnHP/4hZcuWlWuvvVY+/vjjTO8/ffq0jB49Wpo2bWreGxISIt27d5fdu3dnK2v69OnSuHFjs/8rVqwoLVu2zLa8rI4dOyZ169aVJk2ayK+//mpr2wCguL399tvmuKaxJywsTIYPH55jC4P/+7//k4iICFMxd9VVV8l9990nx48fz3P5cXFxUrlyZYmKipI//vgjX+ukx81HH31UGjRoYMqrVKmS9OrV67LXdqWmpsqTTz5pytFj/V133SUnT57MNt+KFSukXbt2Zp7y5cvLbbfdJt99953t2OewadMmadWqlZmvTp068u6770pBEMfgSkEuLQ1wgUceeUT+/e9/y4gRI6RRo0bmgK0H6B9++EFuuOEG0zxPD4oaxMaPH2+a6mmCpAfWjRs3yo033pjjcj/66COZOXOmbN++XWbNmmWm3Xzzzflap9WrV8vhw4dNcqOBRIONLksft23bZhKhu+++W3766SdZsGCBvPbaaya4Kg1mWvaDDz5o1m3o0KFmugaajHr37m0SsJiYGJM06jpqEjlp0iRb++/SpUtm/7Rv314mT54s8+fPN/tSg864ceOkf//+Zl1nzJhhmjfedNNNplyl26jNDzVI6zQNEhoMNVH9/vvvzQ8L9d5778k///lP+Z//+R8ZOXKknD9/3gRcTXD79euX43odOnTIfEZXXnml2Z+O/QMAnkArtbRpdufOnWXYsGHy448/yjvvvCM7duwwFXUlS5Y082kFnMYCTRr0eK3Hyf/93/818+zatctUhuVEl9OtWzfzY33ZsmUmKcoPfZ82G+/bt6/UqFHDJFC6XpqM6XFZk4CMHnvsMZMUaHzUeV9//XUTAxYtWuScR2PSwIEDzfpojNFKO11m27ZtzTZoRWZ+Y58juenatauJd7of09LSTPlVqlQp0GdBHIPLWICPCQ0NtYYPH57ja+np6Va9evWsbt26mXGHP//806pVq5bVpUsX57Q5c+ZY+hU5cuSIc9rAgQOtsmXL2l4nXX5WCxYsMMvfsGGDc9qUKVOylemg5Wr5WY0fP968Z/DgwZmm33XXXValSpVsracuX5c1ceJE57QzZ85YpUuXtgICAqyFCxc6p+/fv9/Mq+U7nD9/3rp06VKmZeq2BAcHWxMmTHBO69Gjh9W4ceNc18WxXSdPnrR++OEHKywszGrVqpV1+vRpW9sEAMUhY4z47bffrFKlSlldu3bNdAx88803zTyzZ882zy9cuGBdffXVVpMmTay//vrLOd/nn39u5nvuuedyjDebNm2yQkJCrNtuu80cZwsbf7Zu3WrKmzt3brbt6dy5c6b4+MQTT1glSpSwzp49a56npKRYFSpUsB566KFMyzxx4oSJvxmn5zf29ezZ07riiiusY8eOOad9//33ply7P1WJY3AlmvbB52htntYIJSYm5tgs4sCBA6a2SM9UaZekOmizuU6dOsmGDRskPT29yNcpY82h1lppmW3atDHPMzY5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"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n",
      "['[BOS]', '[UNK]', 'does', 'the', '[UNK]', 'say', '?', '[EOS]', '[PAD]', '[PAD]', '[PAD]', '[PAD]']\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]])\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ],
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1AAAAG1CAYAAAD3DRUpAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAQFFJREFUeJzt3QucTfX6+PFnxjCuM3TDNFMhl5SQCX/CIINuk1tCIU4KlQrJq+MoyiQVujJ1kBK5TFdpiNxCumA4RW4xYyiOmplcxmXW//V8z2vv38wYZs2Y2XvtvT/v12uZtddee33X3mavZ57vetZ3BVmWZQkAAAAAoEDBBa8CAAAAAFAkUAAAAABgEwkUAAAAANhEAgUAAAAANpFAAQAAAIBNJFAAAAAAYBMJFAAAAADYRAIFAAAAADaRQAEAAACATSRQQD5mzZolQUFB8ttvv+VaPmnSJKlZs6aUKlVKGjVq5LX983fPPvus+fyPHDni7V0BANsxorj0799fKlasKJ5+Pz/88IM4LQ74KuKYfyOBAmxaunSpPPXUU9KyZUuZOXOmTJgwoVi3v27dOnPA/euvv855Ttv65JNPirU9AAAAFB4JFGDTihUrJDg4WP79739L37595bbbbiv2BOq5554jgQIAAHAwEijApj/++EPKlSsnZcqU8fauAAAAwEtIoOBXMjMz5fHHH5drrrlGQkND5YorrpAOHTrITz/95F7nu+++k06dOkl4eLiUL19e2rRpI99+++0Ft6t1zFq2d+zYMTOvk9aM25GcnGzq2fXaqbJly0q1atVkwIAB8t///te9jpbujRw50szXqFHD3YbW1+tPbfe9995zL9ftuV6nj3ft2mWWVa5c2byvBx54QI4fP16kmvv9+/fLHXfcYeavvPJKefPNN83zW7dulXbt2kmFChXk6quvlg8//DDX648ePSojRoyQBg0amNeGhYVJ586dZcuWLee09frrr8v1119vPv8qVapIdHT0OdvLa9++fXLttdfKDTfcIL///nuh3hsAlLS33nrLHNc09kRERMjQoUPzrShYsGCBNGnSxHTIXXbZZXLffffJgQMHCtz+5s2b5fLLL5eYmBj5+++/be2THjeHDBkidevWNe1deuml0qNHj/Neu5WVlSVPPvmkaUeP9V26dJHDhw+fs96SJUukVatWZp1KlSrJ7bffLv/5z38KHftc1q5dKzfffLNZr1atWjJ9+nQpCuIYPCXEYy0BHvDwww/LwoUL5ZFHHpH69eubA7UemH/55Re56aabTBmeHgw1eI0dO9aU5GlipAfUNWvWSNOmTfPd7vvvvy8JCQmyceNGeffdd82yFi1a2NqnZcuWyZ49e0xSowFEg4xuS39u2LDBJEBdu3aVX3/9VebOnSuTJ082QVVpENO2//GPf5h9GzRokFmuASane+65xyRe8fHxJlnUfdTkceLEiYX6/M6ePWs+n9atW8tLL70kc+bMMZ+lBptnnnlG+vTpY/Z12rRppozx//2//2faVfoetcxQg7Mu0+CgQVAT1J9//tn8QaHeeecdeeyxx6R79+4ybNgwOXnypAm0mtj27t073/3avXu3+T+65JJLzOfp+nwAwAm0M0tLsG+99VYZPHiw7NixQ95++235/vvvTQdd6dKlzXra8aaxQJMFPV7rcXLq1KlmnU2bNplOsPzodjp27Gj+SP/0009NMmSHvk7Lw++9916JjIw0iZPulyZhelzWP/5zevTRR00yoPFR150yZYqJAR999JF7HY1J/fr1M/ujMUY763Sbt9xyi3kP2oFpN/a5kprY2FgT7/RzPHPmjGm/atWqRfq/II7BIyzAj4SHh1tDhw7N97ns7Gyrdu3aVseOHc28y/Hjx60aNWpYHTp0cC+bOXOmpV+PvXv3upf169fPqlChQqH3Sbef19y5c832V69e7V42adKkc9p00Xa1/bzGjh1rXjNgwIBcy7t06WJdeumlhdpP3b5ua8KECe5lf/75p1WuXDkrKCjImjdvnnv59u3bzbravsvJkyets2fP5tqmvpfQ0FBr3Lhx7mVxcXHW9ddff8F9cb2vw4cPW7/88osVERFh3XzzzdbRo0cL9Z4AoCTkjBF//PGHVaZMGSs2NjbXMfCNN94w68yYMcM8PnXqlHXFFVdYN9xwg3XixAn3el988YVZ71//+le+8Wbt2rVWWFiYdfvtt5vj7MXGn/Xr15v2Zs+efc77ufXWW3PFxyeeeMIqVaqU9ddff5nHmZmZVuXKla0HH3ww1zYPHTpk4m/O5XZj3913322VLVvW2rdvn3vZzz//bNot7J+pxDF4CiV88Cvae6c9QGlpafmWP+zcudP0DumZKR1aVCctj2vfvr2sXr1asrOzi32fcvYUai+Vttm8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gE0kUAAAAABgEwkUAAAAANhEAgUAAAAANpFAAQAAAIBNJFAAAAAAYBMJFAAAAADYRAIFAAAAAL6QQK1cuVKioqIkISFBYmJipFWrVtK6dWvp3bu3nD171qyzdetWad++vbRp00buuOMOSUlJMcuTk5PNurq8RYsWcuDAAfn555+lUaNGMmLECG++LQCAjyIuAQAcfwaqZ8+eMmjQIDO/ZMkSWb16tVSsWFHWr18vp0+flvvuu88EslWrVsno0aPNYzV+/Hh5++23zfLly5fLpZdeKvXr15cpU6act62srCzJyMjINQEA4K24pIhNAOBbvJ5A5SczM1PCwsJkw4YNpueuVq1aZnnLli0lOzvb9PaVK1dOvv76azl27JiZL1u2bIHbjY+Pl/DwcPekvYwAAHgrLiliEwD4FkclUJ07d5bGjRtLamqqXHfddZKWliYRERG51omMjDTLJ02aJL/88os0bNjQ9BZqwCqI9hSmp6e7J1fZBQAA3ohLitgEAL7FUQmUlkps2rRJevToIa+88opUr17dBKWcNIhp8KpatapMmzZNdu3aJbVr15b333+/wO2HhoaaHsScEwAA3opLitgEAL7FUQmUS5UqVeSPP/6Q5s2by08//SS7d+82y7/99lvzU8sbdu7c6V7/8ssvF8uyvLa/AAD/RlwCALiEiMNKJUqVKmXqyd977z0pU6aMfPDBB/Lggw+a0Y8qVKhgHqt58+bJF198YerMK1eu7F4OAEBxIS4BAByVQOkFtsuWLTOjGenQsfnRWvIVK1acs3zMmDFmykmHi3366aflrrvuKrF9BgD4L+ISAKAgQVYA1xjoULE64lGMxElIUGlv7w6AC0hK2+ztXUAxysjMlip19phBE7jmxxmxie8YgECXYTM2OfIaKAAAAABwIhIoAAAAALCJBAoAAAAAbCKBAgAAAACbSKAAAAAAwCYSKAAAAACwiQQKAAAAAHzhRroAAMAZOkY08kq73H8KgK/hDBQAAAAA2EQCBQAAAAA2kUABAAAAgE0kUAAAAABgEwkUAAAAANhEAgUAAAAANpFAAQAAAIBNJFAAAAAAYBMJFAAAAADYRAIFAAAAADaRQAEAAACATSRQAAAAAOD0BGrlypUSFRUlCQkJEh0dnes51+P+/ftL586dz1murx0xYoSZX79+vbRs2VL++usv6du3r0RGRnr0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"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n"
     ]
    }
   ],
   "execution_count": 87
  },
  {
   "cell_type": "code",
   "source": [
    "(torch.triu(torch.ones(5, 5)) == 0).transpose(-1, -2).bool()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T07:41:03.911238Z",
     "start_time": "2025-02-06T07:41:03.904237Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[False,  True,  True,  True,  True],\n",
       "        [False, False,  True,  True,  True],\n",
       "        [False, False, False,  True,  True],\n",
       "        [False, False, False, False,  True],\n",
       "        [False, False, False, False, False]])"
      ]
     },
     "execution_count": 89,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 89
  },
  {
   "cell_type": "code",
   "source": [
    "#帮我随机两个[5, 1, 1, 4]与[1, 1, 4, 4]尺寸的张量，并求和\n",
    "a = torch.randn(5, 1, 1, 4)\n",
    "b = torch.randn(1, 1, 4, 4)\n",
    "(a + b).shape"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T07:41:00.793112Z",
     "start_time": "2025-02-06T07:41:00.785662Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([5, 1, 4, 4])"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 88
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TelCDOoEN4yF"
   },
   "source": [
    "#### Transformer Model"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "oFGNt8FPN4yF",
    "ExecuteTime": {
     "end_time": "2025-02-06T07:56:52.261967Z",
     "start_time": "2025-02-06T07:56:52.235464Z"
    }
   },
   "source": [
    "@dataclass\n",
    "class TransformerOutput:\n",
    "    logits: Tensor\n",
    "    encoder_last_hidden_states: Tensor\n",
    "    encoder_attn_scores: List[Tensor] #画图\n",
    "    decoder_last_hidden_states: Tensor\n",
    "    decoder_self_attn_scores: List[Tensor] #画图\n",
    "    decoder_cross_attn_scores: List[Tensor] #画图\n",
    "    preds: Optional[Tensor] = None\n",
    "\n",
    "class TransformerModel(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"]\n",
    "        self.num_encoder_layers = config[\"num_encoder_layers\"]\n",
    "        self.num_decoder_layers = config[\"num_decoder_layers\"]\n",
    "        self.pad_idx = config[\"pad_idx\"]\n",
    "        self.bos_idx = config[\"bos_idx\"]\n",
    "        self.eos_idx = config[\"eos_idx\"]\n",
    "        self.vocab_size = config[\"vocab_size\"]\n",
    "        self.dropout_rate = config[\"dropout\"]\n",
    "        self.max_length = config[\"max_length\"]\n",
    "        self.share = config[\"share_embedding\"] # 是否共享词嵌入，共享后，decoder的词嵌入层和encoder的词嵌入层相同，节省内存\n",
    "\n",
    "        # layers\n",
    "        self.src_embedding = TransformerEmbedding(config) # 输入的嵌入层\n",
    "        if self.share:#如果共享词嵌入，则使用src_embedding作为trg_embedding\n",
    "            self.trg_embedding = self.src_embedding #源和目标的嵌入层相同，共享参数，节省内存\n",
    "            self.linear = lambda x: torch.matmul(\n",
    "                x, self.trg_embedding.get_word_embedding_weights().T\n",
    "            ) # 输出层，共享参数，直接拿原有embedding矩阵的转置，节省内存\n",
    "        else:\n",
    "            self.trg_embedding = TransformerEmbedding(config) #decoder模块的嵌入层\n",
    "            self.linear = nn.Linear(self.hidden_size, self.vocab_size) # 输出层\n",
    "\n",
    "        self.encoder = TransformerEncoder(config)\n",
    "        self.decoder = TransformerDecoder(config)\n",
    "\n",
    "        # init weights\n",
    "        self._init_weights()\n",
    "\n",
    "    def _init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化权重\"\"\"\n",
    "        for p in self.parameters():\n",
    "            if p.dim() > 1:\n",
    "                nn.init.xavier_uniform_(p)\n",
    "\n",
    "    def generate_square_subsequent_mask(self, sz: int) -> Tensor:\n",
    "        \"\"\"\n",
    "        Generate a square mask for the sequence. The masked positions are filled with True.\n",
    "            Unmasked positions are filled with False.为了生成斜三角的mask\n",
    "        \"\"\"\n",
    "        mask = (torch.triu(torch.ones(sz, sz)) == 0).transpose(-1, -2).bool()\n",
    "\n",
    "        return mask\n",
    "\n",
    "    def forward(\n",
    "        self, encoder_inputs, decoder_inputs, encoder_inputs_mask=None\n",
    "    ) -> TransformerOutput:\n",
    "        # encoder_inputs: [batch_size, src_len]\n",
    "        # decoder_inputs: [batch_size, trg_len]\n",
    "        # encoder_inputs_mask: [batch_size, src_len]\n",
    "        if encoder_inputs_mask is None:\n",
    "            encoder_inputs_mask = encoder_inputs.eq(self.pad_idx) # [batch_size, src_len]\n",
    "        encoder_inputs_mask = encoder_inputs_mask.unsqueeze(1).unsqueeze(\n",
    "            2\n",
    "        )  # [batch_size, 1, 1, src_len],用于encoder的自注意力\n",
    "        look_ahead_mask = self.generate_square_subsequent_mask(decoder_inputs.shape[1])\n",
    "        look_ahead_mask = (\n",
    "            look_ahead_mask.unsqueeze(0).unsqueeze(0).to(decoder_inputs.device)\n",
    "        )  #[trg_len, trg_len]--> [1, 1, trg_len, trg_len],用于decoder的自注意力\n",
    "        #增加decoder_inputs_mask和look_ahead_mask进行组合\n",
    "        decoder_inputs_mask = decoder_inputs.eq(self.pad_idx) # [batch_size, trg_len]，和上面encoder_inputs_mask一致\n",
    "        # print(decoder_inputs_mask.shape)\n",
    "        decoder_inputs_mask = decoder_inputs_mask.unsqueeze(1).unsqueeze(2)  # [batch_size, 1, 1, trg_len]\n",
    "        # print(decoder_inputs_mask.shape)\n",
    "        decoder_inputs_mask = decoder_inputs_mask + look_ahead_mask # [batch_size, 1, 1, trg_len]与[1, 1, trg_len, trg_len]相加，得到decoder的自注意力mask\n",
    "\n",
    "        # encoding\n",
    "        encoder_inputs_embeds = self.src_embedding(encoder_inputs)\n",
    "        encoder_outputs = self.encoder(encoder_inputs_embeds, encoder_inputs_mask) #encoder_inputs_mask用于encoder的自注意力,广播去做计算\n",
    "\n",
    "        # decoding\n",
    "        decoder_inputs_embeds = self.trg_embedding(decoder_inputs)\n",
    "        decoder_outputs = self.decoder(\n",
    "            decoder_inputs_embeds=decoder_inputs_embeds,\n",
    "            encoder_outputs=encoder_outputs.last_hidden_states,\n",
    "            attn_mask=decoder_inputs_mask, #用于decoder的自注意力,广播去做计算\n",
    "            cross_attn_mask=encoder_inputs_mask,#用于decoder的交叉注意力,广播去做计算\n",
    "        )\n",
    "\n",
    "        logits = self.linear(decoder_outputs.last_hidden_states) # [batch_size, trg_len, vocab_size]\n",
    "\n",
    "        return TransformerOutput(\n",
    "            logits=logits,\n",
    "            encoder_last_hidden_states=encoder_outputs.last_hidden_states,\n",
    "            encoder_attn_scores=encoder_outputs.attn_scores,\n",
    "            decoder_last_hidden_states=decoder_outputs.last_hidden_states,\n",
    "            decoder_self_attn_scores=decoder_outputs.self_attn_scores,\n",
    "            decoder_cross_attn_scores=decoder_outputs.cross_attn_scores,\n",
    "        )\n",
    "\n",
    "    @torch.no_grad()\n",
    "    def infer(self, encoder_inputs, encoder_inputs_mask=None) -> Tensor:\n",
    "        # assert len(encoder_inputs.shape) == 2 and encoder_inputs.shape[0] == 1\n",
    "        if encoder_inputs_mask is None:#应对多个样本同时进行推理\n",
    "            encoder_inputs_mask = encoder_inputs.eq(self.pad_idx)\n",
    "        encoder_inputs_mask = encoder_inputs_mask.unsqueeze(1).unsqueeze(2)  # [batch_size, 1, 1, src_len],[1,src_len]相加时，会自动广播到[batch_size,1,src_len,src_len]\n",
    "        look_ahead_mask = self.generate_square_subsequent_mask(self.max_length)\n",
    "        look_ahead_mask = (\n",
    "            look_ahead_mask.unsqueeze(0).unsqueeze(0).to(encoder_inputs.device)\n",
    "        )  # [1, 1, trg_len, trg_len]\n",
    "\n",
    "        # encoding\n",
    "        encoder_inputs_embeds = self.src_embedding(encoder_inputs)\n",
    "        encoder_outputs = self.encoder(encoder_inputs_embeds) #因为只支持单样本预测，没有paddings，所以不需要mask\n",
    "\n",
    "        # decoding,多样本推理\n",
    "        decoder_inputs = torch.Tensor([self.bos_idx] * encoder_inputs.shape[0]).reshape(-1, 1).long().to(device=encoder_inputs.device)\n",
    "        for cur_len in tqdm(range(1, self.max_length + 1)):\n",
    "            decoder_inputs_embeds = self.trg_embedding(decoder_inputs)\n",
    "            decoder_outputs = self.decoder(\n",
    "                decoder_inputs_embeds=decoder_inputs_embeds,\n",
    "                encoder_outputs=encoder_outputs.last_hidden_states,\n",
    "                attn_mask=look_ahead_mask[:, :, :cur_len, :cur_len],#decoder的自注意力mask\n",
    "            )\n",
    "\n",
    "            logits = self.linear(decoder_outputs.last_hidden_states)\n",
    "            next_token = logits.argmax(dim=-1)[:, -1:] #通过最大下标确定类别，[:, -1:]表示取最后一个结果\n",
    "            decoder_inputs = torch.cat([decoder_inputs, next_token], dim=-1) #预测输出拼接到输入中\n",
    "            #(decoder_inputs == self.eos_idx).sum(dim=-1)是判断样本中是否含有EOS标记\n",
    "            #all是每一个都为True，才会结束\n",
    "            if all((decoder_inputs == self.eos_idx).sum(dim=-1) > 0):\n",
    "                break\n",
    "\n",
    "        return TransformerOutput(\n",
    "            preds=decoder_inputs[:, 1:],\n",
    "            logits=logits,\n",
    "            encoder_last_hidden_states=encoder_outputs.last_hidden_states,\n",
    "            encoder_attn_scores=encoder_outputs.attn_scores,\n",
    "            decoder_last_hidden_states=decoder_outputs.last_hidden_states,\n",
    "            decoder_self_attn_scores=decoder_outputs.self_attn_scores,\n",
    "            decoder_cross_attn_scores=decoder_outputs.cross_attn_scores,\n",
    "        )"
   ],
   "outputs": [],
   "execution_count": 90
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "kXHQ3sGTN4yG"
   },
   "source": [
    "## 训练"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "OxNA8DIzN4yG"
   },
   "source": [
    "### 损失函数"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "1xzahHANN4yG",
    "ExecuteTime": {
     "end_time": "2025-02-06T08:00:18.862754Z",
     "start_time": "2025-02-06T08:00:18.850553Z"
    }
   },
   "source": [
    "class CrossEntropyWithPadding:\n",
    "    def __init__(self, config):\n",
    "        self.label_smoothing = config[\"label_smoothing\"]\n",
    "\n",
    "    def __call__(self, logits, labels, padding_mask=None):\n",
    "        # logits.shape = [batch size, sequence length, num of classes]\n",
    "        # labels.shape = [batch size, sequence length]\n",
    "        # padding_mask.shape = [batch size, sequence length]\n",
    "        bs, seq_len, nc = logits.shape\n",
    "        loss = F.cross_entropy(logits.reshape(bs * seq_len, nc), labels.reshape(-1), reduce=False, label_smoothing=self.label_smoothing) #label_smoothing表示随机将一个类别的概率设置为0.1，使得模型更加关注其他类别\n",
    "        if padding_mask is None:\n",
    "            loss = loss.mean()\n",
    "        else:\n",
    "            padding_mask = 1 - padding_mask.reshape(-1) #将padding_mask reshape成一维张量，mask部分为0，非mask部分为1\n",
    "            loss = torch.mul(loss, padding_mask).sum() / padding_mask.sum()\n",
    "\n",
    "        return loss\n"
   ],
   "outputs": [],
   "execution_count": 91
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zuBC9KqFN4yG"
   },
   "source": [
    "### 学习率衰减"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "x=np.arange(1, 40000)\n",
    "plt.plot(x, x * (4000 ** (-1.5)))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T08:00:22.231423Z",
     "start_time": "2025-02-06T08:00:22.023564Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x203b7df14f0>]"
      ]
     },
     "execution_count": 92,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ],
      "image/png": "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TVPbsubAwEDNb7/9ptm/f7+cJV99ya+YIS+W/L744otyGaM416J/dy/5NTU11Sxbtkz2WazGaqxlzQ/qo3jurbfekjP1xTn9+eefNUFBQbIPJSUlOtHH1157TS49F5/L6stCi4uLq+o01+eyKb7HD+vfhQsXNO+//77slziH4rPq6empGThwoE70T5g3b55c9STaL75noixWof300086f/4e1j99OH+1uXvlk66dQwYsDyHWlIsTKtaQi6VZYs8LbSOWkLVt21a20dXVVZbFF66SuJC//vrrcsme+GA9//zz8h/X6q5cuaJ5+umn5T4dItgRQVB5eXmNOnv27NEEBATInyO+vGIfiqYgfo64iN/9EEt9K5c2L1q0SF6MxZdgyJAhch+F6m7evCkv3ra2tnIJ3pQpU2QgUJ3Yw6V///7yPcT/NxEI3e3rr7/WdOrUSfZZLN0T+zY0dR/FRU/8AyH+YRDBQ4cOHeS+BXd/ubW5j7X1TTyqf2aa83PZ2N/jh/UvJSVFXtxatWol/9+LPXLEP+jV9/HQ5v4Jf/jDH+RnT7yn+CyK71llsKLr5+9h/dOH81eXgEXXzqGR+E/DBpyIiIiImgfnsBAREZHWY8BCREREWo8BCxEREWk9BixERESk9RiwEBERkdZjwEJERERajwELERERaT0GLERERKT1GLAQERGR1mPAQkRERFqPAQsRERFpPQYsREREBG33/1r4RkQF2dtnAAAAAElFTkSuQmCC"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 92
  },
  {
   "cell_type": "code",
   "source": [
    "np.sqrt(512)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T08:00:26.673777Z",
     "start_time": "2025-02-06T08:00:26.649155Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "22.627416997969522"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 93
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 449
    },
    "id": "UQPiKK4nN4yG",
    "outputId": "63cfb132-ef75-4f94-f0c8-86a2f36e09c2",
    "ExecuteTime": {
     "end_time": "2025-02-06T08:00:33.573606Z",
     "start_time": "2025-02-06T08:00:33.438604Z"
    }
   },
   "source": [
    "# NoamDecayScheduler 是一个自定义或外部定义的学习率衰减调度器类。它需要接收配置 config 作为参数，可能实现了特定的学习率衰减方案\n",
    "class NoamDecayScheduler:\n",
    "    def __init__(self, config):\n",
    "        self.d_model = config[\"d_model\"]\n",
    "        self.warmup_steps = config[\"warmup_steps\"]\n",
    "\n",
    "    def __call__(self, step):\n",
    "        step += 1\n",
    "        arg1 = step ** (-0.5) #4000步之后是arg1\n",
    "        arg2 = step * (self.warmup_steps ** (-1.5))  #4000步之前是arg2\n",
    "\n",
    "        arg3 = self.d_model ** (-0.5)\n",
    "\n",
    "        return arg3 * np.minimum(arg1, arg2)\n",
    "\n",
    "\n",
    "temp_learning_rate_schedule = NoamDecayScheduler({\"d_model\": 512, \"warmup_steps\": 4000})\n",
    "#下面是学习率的设计图\n",
    "plt.plot(temp_learning_rate_schedule(np.arange(0, 40000)))\n",
    "plt.ylabel(\"Leraning rate\")\n",
    "plt.xlabel(\"Train step\")\n",
    "plt.show()\n"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ],
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 94
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "XHj_EjzlN4yW"
   },
   "source": [
    "### 优化器"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "1EVLKx2rN4yW",
    "ExecuteTime": {
     "end_time": "2025-02-06T08:00:37.039131Z",
     "start_time": "2025-02-06T08:00:37.032548Z"
    }
   },
   "source": [
    "from torch.optim.lr_scheduler import LambdaLR\n",
    "from torch.optim import Adam\n",
    "\n",
    "def get_optimizer(model, config):\n",
    "    base_lr = 0.1\n",
    "    beta1 = config[\"beta1\"] # Adam 的 beta1\n",
    "    beta2 = config[\"beta2\"] # Adam 的 beta2\n",
    "    eps = config[\"eps\"]\n",
    "    optimizer = Adam(model.parameters(), lr=base_lr, betas=(beta1, beta2), eps=eps)\n",
    "    lr_scheduler = NoamDecayScheduler(config) #config是一个字典，包含了学习率衰减的参数\n",
    "    # 使用 LambdaLR 调度器，它可以根据给定的函数 lr_lambda 调整学习率。这里将 lr_scheduler 作为函数传递给 LambdaLR，它包含了特定于模型或任务的学习率调度规则\n",
    "    scheduler = LambdaLR(optimizer, lr_lambda=lr_scheduler)\n",
    "    return optimizer, scheduler"
   ],
   "outputs": [],
   "execution_count": 95
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "JBhiO7O2N4yW"
   },
   "source": [
    "### Callback"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "rWFMJwBkN4yX",
    "ExecuteTime": {
     "end_time": "2025-02-06T08:01:16.523049Z",
     "start_time": "2025-02-06T08:00:41.442439Z"
    }
   },
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "\n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\",\n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "\n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "\n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "\n",
    "        )\n",
    "\n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ],
   "outputs": [],
   "execution_count": 96
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "64y_NBHMN4yX",
    "ExecuteTime": {
     "end_time": "2025-02-06T08:01:22.369230Z",
     "start_time": "2025-02-06T08:01:22.363379Z"
    }
   },
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch.\n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = - np.inf\n",
    "\n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "\n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "\n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 97
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "Uk4PEb70N4yX",
    "ExecuteTime": {
     "end_time": "2025-02-06T08:01:27.924907Z",
     "start_time": "2025-02-06T08:01:27.919396Z"
    }
   },
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute\n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = - np.inf\n",
    "        self.counter = 0\n",
    "\n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter\n",
    "            self.counter = 0\n",
    "        else:\n",
    "            self.counter += 1\n",
    "\n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ],
   "outputs": [],
   "execution_count": 98
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_AB84Qx1N4yX"
   },
   "source": [
    "### training & valuating"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "1mKPSFkON4yX",
    "ExecuteTime": {
     "end_time": "2025-02-06T08:01:32.991918Z",
     "start_time": "2025-02-06T08:01:32.983563Z"
    }
   },
   "source": [
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    for batch in dataloader:\n",
    "        encoder_inputs = batch[\"encoder_inputs\"]\n",
    "        encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "        decoder_inputs = batch[\"decoder_inputs\"]\n",
    "        decoder_labels = batch[\"decoder_labels\"]\n",
    "        decoder_labels_mask = batch[\"decoder_labels_mask\"]\n",
    "\n",
    "        # 前向计算\n",
    "        outputs = model(\n",
    "            encoder_inputs=encoder_inputs,\n",
    "            decoder_inputs=decoder_inputs,\n",
    "            encoder_inputs_mask=encoder_inputs_mask\n",
    "            )\n",
    "        logits = outputs.logits\n",
    "        loss = loss_fct(logits, decoder_labels, padding_mask=decoder_labels_mask)         # 验证集损失\n",
    "        loss_list.append(loss.cpu().item())\n",
    "\n",
    "    return np.mean(loss_list)\n"
   ],
   "outputs": [],
   "execution_count": 99
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "PClBmtgWN4yY",
    "ExecuteTime": {
     "end_time": "2025-02-06T08:01:58.470725Z",
     "start_time": "2025-02-06T08:01:58.459704Z"
    }
   },
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model,\n",
    "    train_loader,\n",
    "    val_loader,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    scheduler=None,\n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "\n",
    "    global_step = 1\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for batch in train_loader:\n",
    "                encoder_inputs = batch[\"encoder_inputs\"]\n",
    "                encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "                decoder_inputs = batch[\"decoder_inputs\"]\n",
    "                decoder_labels = batch[\"decoder_labels\"]\n",
    "                decoder_labels_mask = batch[\"decoder_labels_mask\"]\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "\n",
    "                # 前向计算\n",
    "                outputs = model(\n",
    "                    encoder_inputs=encoder_inputs,\n",
    "                    decoder_inputs=decoder_inputs,\n",
    "                    encoder_inputs_mask=encoder_inputs_mask\n",
    "                    )\n",
    "                logits = outputs.logits\n",
    "                loss = loss_fct(logits, decoder_labels, padding_mask=decoder_labels_mask)\n",
    "\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                if scheduler is not None:\n",
    "                    scheduler.step() # 更新学习率\n",
    "\n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"step\": global_step\n",
    "                })\n",
    "\n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "\n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    cur_lr = optimizer.param_groups[0][\"lr\"] if scheduler is None else scheduler.get_last_lr()[0]\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step,\n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            lr=cur_lr,\n",
    "                            )\n",
    "\n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=-val_loss)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(-val_loss)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "\n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "            pbar.set_postfix({\"epoch\": epoch_id, \"loss\": loss, \"val_loss\": val_loss})\n",
    "\n",
    "    return record_dict\n"
   ],
   "outputs": [],
   "execution_count": 100
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "khZqMS8pN4yY",
    "outputId": "2367a363-585b-4321-f7ea-cb11d9623008",
    "ExecuteTime": {
     "end_time": "2025-02-06T08:02:02.668610Z",
     "start_time": "2025-02-06T08:02:02.153513Z"
    }
   },
   "source": [
    "#模型的超参\n",
    "config = {\n",
    "    \"bos_idx\": 1,\n",
    "    \"eos_idx\": 3,\n",
    "    \"pad_idx\": 0,\n",
    "    \"vocab_size\": len(word2idx),\n",
    "    \"max_length\": 128,\n",
    "    \"d_model\": 512,\n",
    "    \"dim_feedforward\": 2048, # FFN 的隐藏层大小\n",
    "    \"dropout\": 0.1,\n",
    "    \"layer_norm_eps\": 1e-6, # 层归一化的 epsilon, 防止除零错误\n",
    "    \"num_heads\": 8,\n",
    "    \"num_decoder_layers\": 6,\n",
    "    \"num_encoder_layers\": 6,\n",
    "    \"label_smoothing\": 0.1,\n",
    "    \"beta1\": 0.9, # Adam 的 beta1\n",
    "    \"beta2\": 0.98,\n",
    "    \"eps\": 1e-9,\n",
    "    \"warmup_steps\": 4_000,\n",
    "    \"share_embedding\": False, # 是否共享词向量\n",
    "    }\n",
    "\n",
    "\n",
    "def get_dl(dataset, batch_size, shuffle=True):\n",
    "    sampler = TransformerBatchSampler(dataset, batch_size=batch_size, shuffle_batch=shuffle)\n",
    "    sample_dl = DataLoader(dataset, batch_sampler=sampler, collate_fn=partial(collate_fct, tokenizer=tokenizer))\n",
    "    return sample_dl\n",
    "\n",
    "# dataset\n",
    "train_ds = LangPairDataset(\"train\", max_length=config[\"max_length\"])\n",
    "val_ds = LangPairDataset(\"val\", max_length=config[\"max_length\"])\n",
    "# tokenizer\n",
    "tokenizer = Tokenizer(word2idx=word2idx, idx2word=idx2word, max_length=config[\"max_length\"])\n",
    "batch_size = 2048\n",
    "# dataloader\n",
    "train_dl = get_dl(train_ds, batch_size=batch_size, shuffle=True)\n",
    "val_dl = get_dl(val_ds, batch_size=batch_size, shuffle=False)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load train dataset from wmt16\\.cache\\de2en_train_128.npy\n",
      "load val dataset from wmt16\\.cache\\de2en_val_128.npy\n"
     ]
    }
   ],
   "execution_count": 101
  },
  {
   "cell_type": "code",
   "source": [
    "#计算模型参数量\n",
    "model = TransformerModel(config)\n",
    "print(f\"模型参数量: {sum(p.numel() for p in model.parameters() if p.requires_grad)}\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-06T08:02:07.021313Z",
     "start_time": "2025-02-06T08:02:05.973480Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "模型参数量: 71938239\n"
     ]
    }
   ],
   "execution_count": 102
  },
  {
   "cell_type": "code",
   "source": [
    "config"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "63slV04gWLn8",
    "outputId": "5c5ede03-0f6f-48d5-a177-d961886ec340",
    "ExecuteTime": {
     "end_time": "2025-02-06T08:02:10.696555Z",
     "start_time": "2025-02-06T08:02:10.690995Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'bos_idx': 1,\n",
       " 'eos_idx': 3,\n",
       " 'pad_idx': 0,\n",
       " 'vocab_size': 18111,\n",
       " 'max_length': 128,\n",
       " 'd_model': 512,\n",
       " 'dim_feedforward': 2048,\n",
       " 'dropout': 0.1,\n",
       " 'layer_norm_eps': 1e-06,\n",
       " 'num_heads': 8,\n",
       " 'num_decoder_layers': 6,\n",
       " 'num_encoder_layers': 6,\n",
       " 'label_smoothing': 0.1,\n",
       " 'beta1': 0.9,\n",
       " 'beta2': 0.98,\n",
       " 'eps': 1e-09,\n",
       " 'warmup_steps': 4000,\n",
       " 'share_embedding': False}"
      ]
     },
     "execution_count": 103,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 103
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 49,
     "referenced_widgets": [
      "55146ac7395840dca847e0f63aa80561",
      "abb9bc76c0ce4634bd340be20c074b8c",
      "a89d790df59245fcaf26335d4903c9b4",
      "f837255914194dc792cf1514dee03dc5",
      "46a51a1719d8444eb5877720253d3c37",
      "013d2a6b90144432bbfb15136235faba",
      "2b82e42c34804b13bc83be4c7009d4e2",
      "9233439bdd6746c28ad4a20ef47799f6",
      "0e10428326bc436e8dbc079e89570bde",
      "0fe4ed7283c04496affe727c235d31c4",
      "0c0c7229347b49ffbb9a0f7bd41199b9"
     ]
    },
    "id": "tVO2zJ04N4yY",
    "outputId": "7d4079f5-65d4-4da1-9001-91aa5525250e"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "  0%|          | 0/280 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "55146ac7395840dca847e0f63aa80561"
      }
     },
     "metadata": {}
    }
   ],
   "source": [
    "epoch = 20\n",
    "\n",
    "# model\n",
    "model = TransformerModel(config)\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = CrossEntropyWithPadding(config)\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer, scheduler = get_optimizer(model, config)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "exp_name = \"translate-transformer-{}\".format(\"share\" if config[\"share_embedding\"] else \"not-share\")\n",
    "tensorboard_callback = TensorBoardCallback(f\"runs/{exp_name}\")\n",
    "# tensorboard_callback.draw_model(model, [1, MAX_LENGTH])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\n",
    "    f\"checkpoints/{exp_name}\", save_step=500, save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=8)\n",
    "\n",
    "model = model.to(device)\n",
    "\n",
    "record = training(\n",
    "    model,\n",
    "    train_dl,\n",
    "    val_dl,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    scheduler,\n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500\n",
    "    )\n",
    "\n",
    "# Training took 3.5 days on 8 P100 GPUs\n",
    "# We trained the base models for a total of 100,000 steps or 12 hours. For our big models,(described on the bottom line of table 3), step time was 1.0 seconds. The big models were trained for 300,000 steps (3.5 days)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rTjbeFlIN4yZ"
   },
   "source": [
    "## 推理\n",
    "\n",
    "- 翻译项目的评估指标一般是BLEU4，感兴趣的同学自行了解并实现\n",
    "- 接下来进行翻译推理，并作出注意力的热度图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "WxsjkyQgN4yZ",
    "outputId": "5ce160c2-ce7e-4bc3-d2b6-a332c7fcf6a1"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Collecting fastBPE\n",
      "  Downloading fastBPE-0.1.0.tar.gz (35 kB)\n",
      "  Preparing metadata (setup.py) ... \u001B[?25l\u001B[?25hdone\n",
      "Building wheels for collected packages: fastBPE\n",
      "  Building wheel for fastBPE (setup.py) ... \u001B[?25l\u001B[?25hdone\n",
      "  Created wheel for fastBPE: filename=fastBPE-0.1.0-cp310-cp310-linux_x86_64.whl size=806588 sha256=b77d4a1091f1aa812ca7c62cb09e4397a6cb8ae13061a3c1aeb5bc104e3d8dc7\n",
      "  Stored in directory: /root/.cache/pip/wheels/13/5d/b9/4b8897941ebc9e8c6cc3f3ffd3ea5115731754269205098754\n",
      "Successfully built fastBPE\n",
      "Installing collected packages: fastBPE\n",
      "Successfully installed fastBPE-0.1.0\n"
     ]
    }
   ],
   "source": [
    "# !pip install Cython  # if failed to install fastBPE, try this line\n",
    "!pip install fastBPE #分词使用\n",
    "# 在 Windows 系统上并没有 sys/mman.h 文件"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "exp_name"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 35
    },
    "id": "f6Ry1PcAn4k1",
    "outputId": "6393524e-e5c3-4f93-dd9d-f249ca52bc05"
   },
   "execution_count": null,
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "'translate-transformer-not-share'"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      }
     },
     "metadata": {},
     "execution_count": 54
    }
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "id": "jT1Mqiq3N4yZ",
    "ExecuteTime": {
     "end_time": "2024-08-05T03:10:32.326542400Z",
     "start_time": "2024-08-05T03:10:31.006376300Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "\n",
    "state_dict = torch.load(f\"best.ckpt\", map_location=\"cpu\")\n",
    "\n",
    "# state_dict1 = torch.load(\"epoch125-step132426.ckpt\", map_location=\"cpu\")\n",
    "# state_dict = state_dict1[\"state_dict\"]\n",
    "\n",
    "# update keys by dropping `model`\n",
    "# for key in list(state_dict):\n",
    "#     state_dict[key.replace(\"model.\", \"\")] = state_dict.pop(key)\n"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "!pip install nltk"
   ],
   "metadata": {
    "id": "eKwIDdjgvTlO",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "outputId": "35979ff2-a16d-4aa4-881a-8496e39e2469",
    "ExecuteTime": {
     "end_time": "2024-08-05T03:10:46.720508800Z",
     "start_time": "2024-08-05T03:10:36.179313900Z"
    }
   },
   "execution_count": 52,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Defaulting to user installation because normal site-packages is not writeable\n",
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Collecting nltk\n",
      "  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/a6/0a/0d20d2c0f16be91b9fa32a77b76c60f9baf6eba419e5ef5deca17af9c582/nltk-3.8.1-py3-none-any.whl (1.5 MB)\n",
      "Requirement already satisfied: click in c:\\users\\administrator\\appdata\\roaming\\python\\python312\\site-packages (from nltk) (8.1.7)\n",
      "Requirement already satisfied: joblib in c:\\users\\administrator\\appdata\\roaming\\python\\python312\\site-packages (from nltk) (1.4.2)\n",
      "Requirement already satisfied: regex>=2021.8.3 in c:\\users\\administrator\\appdata\\roaming\\python\\python312\\site-packages (from nltk) (2024.7.24)\n",
      "Requirement already satisfied: tqdm in c:\\users\\administrator\\appdata\\roaming\\python\\python312\\site-packages (from nltk) (4.66.4)\n",
      "Requirement already satisfied: colorama in c:\\users\\administrator\\appdata\\roaming\\python\\python312\\site-packages (from click->nltk) (0.4.6)\n",
      "Installing collected packages: nltk\n",
      "Successfully installed nltk-3.8.1\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n",
      "[notice] A new release of pip is available: 24.1.2 -> 24.2\n",
      "[notice] To update, run: python.exe -m pip install --upgrade pip\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "!rm -r wmt16/.cache"
   ],
   "metadata": {
    "id": "WVksjIhL_pt9"
   },
   "execution_count": 55,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "tokenizer.decode([[   5,   16,    6,   23,  150,   80, 8248,   35,  232,    4,    3]])"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "2MY69-6WWSjp",
    "outputId": "911d9066-7917-4917-b5cc-d679a1c63865"
   },
   "execution_count": 38,
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "['a man in an orange hat starring at something .']"
      ]
     },
     "metadata": {},
     "execution_count": 38
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "from nltk.translate.bleu_score import sentence_bleu\n",
    "# load checkpoints\n",
    "model = TransformerModel(config)\n",
    "model.load_state_dict(state_dict)\n",
    "\n",
    "loss_fct = CrossEntropyWithPadding(config)\n",
    "# from dataset import LangPairDataset\n",
    "test_ds = LangPairDataset(\"test\", max_length=128, data_dir=\"./wmt16\")\n",
    "test_dl = DataLoader(test_ds, batch_size=1, collate_fn=partial(collate_fct, tokenizer=tokenizer))\n",
    "\n",
    "model = model.to(device)\n",
    "model.eval()\n",
    "collect = {}\n",
    "loss_collect = []\n",
    "\n",
    "predictions = []\n",
    "answers = []\n",
    "# 初始化BLEU分数列表\n",
    "bleu_scores = []\n",
    "for idx, batch in tqdm(enumerate(test_dl)):\n",
    "    encoder_inputs = batch[\"encoder_inputs\"]\n",
    "    encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "    decoder_inputs = batch[\"decoder_inputs\"]\n",
    "    decoder_labels = batch[\"decoder_labels\"]\n",
    "    # print(decoder_labels.cpu())\n",
    "    # decoder_labels1=tokenizer.decode(decoder_labels.cpu().numpy())\n",
    "    # print(decoder_labels1)\n",
    "    # 前向计算\n",
    "    outputs = model(\n",
    "        encoder_inputs=encoder_inputs,\n",
    "        decoder_inputs=decoder_inputs,\n",
    "        encoder_inputs_mask=encoder_inputs_mask\n",
    "        )\n",
    "    loss = loss_fct(outputs.logits, decoder_labels)         # 验证集损失\n",
    "\n",
    "    # print(outputs.logits.shape, decoder_labels.shape)\n",
    "\n",
    "    # loss = loss_fct(outputs.logits[:, :decoder_labels.shape[1]], decoder_labels)         # 验证集损失\n",
    "    # outputs = model.infer(encoder_inputs=encoder_inputs)\n",
    "    # print(outputs.logits.shape)\n",
    "    preds = outputs.logits.argmax(dim=-1) # 预测结果，[1,seq_len]\n",
    "    # print(preds.shape)\n",
    "    #把preds转为英文单词\n",
    "    preds = tokenizer.decode(preds.cpu().numpy()) #['预测句子']\n",
    "    # predictions.append(preds)\n",
    "    # print(preds)\n",
    "    #把decoder_labels转为英文单词\n",
    "    decoder_labels = tokenizer.decode(decoder_labels.cpu().numpy()) #['标签句子']\n",
    "    # answers.append(decoder_labels)\n",
    "    # print(decoder_labels)\n",
    "    belu=sentence_bleu([decoder_labels[0].split()],preds[0].split(),weights=(1, 0, 0, 0))\n",
    "    bleu_scores.append(belu)\n",
    "    collect[idx] = {\"loss\": loss.item(), \"src_inputs\": encoder_inputs, \"trg_inputs\": decoder_inputs, \"mask\": encoder_inputs_mask, \"trg_labels\": decoder_labels, \"preds\": preds}\n",
    "    loss_collect.append(loss.item())\n",
    "    # break\n",
    "\n",
    "# sort collect by value\n",
    "collect = sorted(collect.items(), key=lambda x: x[1][\"loss\"])\n",
    "print(f\"testing loss: {np.array(loss_collect).mean()}\")\n",
    "sum(bleu_scores) / len(bleu_scores)"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 101,
     "referenced_widgets": [
      "c9db4cd83b7b4f53a0fa93b8c365debd",
      "a972aba7913b48c6973ae27bff5b9d1d",
      "22aa774ab39e4fd78b0ff08d430d2b5d",
      "9a01f02fb99b4b81ac487d23bc5daf4a",
      "19f846338f6b48c9930b58575641e5b8",
      "7d293204dc1b4d97bb5ee670787c7b7d",
      "ea8fa440f320435aa75c9bef75bc3f08",
      "4e64bcb87e584beda0408a0aa5a659ae",
      "b077d7562d814df5b6d839464dd4122f",
      "5a28babf4e2e4393bdc1a093034885ab",
      "be07d518c21149d196c610fd57b1eaec"
     ]
    },
    "id": "5QgodlOJfgKj",
    "outputId": "2907bad2-7cb1-4295-c8b0-77ac0dabf398"
   },
   "execution_count": 46,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "load test dataset from wmt16/.cache/de2en_test_128.npy\n"
     ]
    },
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "0it [00:00, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "c9db4cd83b7b4f53a0fa93b8c365debd"
      }
     },
     "metadata": {}
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "testing loss: 3.258438676626829\n"
     ]
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "0.5708733100076216"
      ]
     },
     "metadata": {},
     "execution_count": 46
    }
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 535,
     "referenced_widgets": [
      "40f7e3754ad44b33acfe0a80b3648a3a",
      "b1e51beac50f4947a9dd235d8aa87603",
      "8cd9e9702e114ebc856f56975544ede8",
      "b7c7a718a8654956947108969557ca83",
      "20a27d1ec76f4d85b1ffe99401fbb5b7",
      "e0ddf3970b3e4248bc87d4649fa9c448",
      "d7bdde54e2534c49a207540bd258d3ae",
      "22484000400842f0b6e245dc610acbba",
      "a66a5b4ff37d4dafa4ba3081aa1359f4",
      "0b7866c29e5f4afe9b92de15f1b88c3e",
      "333e84a304184c57af1821620ae80f6a"
     ]
    },
    "id": "KGSym4CbN4ya",
    "outputId": "f5e9c4c2-c92e-44e6-c83a-5cb720305428",
    "ExecuteTime": {
     "end_time": "2024-05-06T13:16:14.099749Z",
     "start_time": "2024-05-06T13:16:13.991811900Z"
    }
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "  0%|          | 0/128 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "40f7e3754ad44b33acfe0a80b3648a3a"
      }
     },
     "metadata": {}
    },
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ],
      "image/png": "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f/NLrh0+/PBDPfvss3ryySfPuaxNmzb1extyc3N15MiRiz6EESVXSVifSqOCggIdP378os57vBQUPGfo2bOnqlSp4j2eet++fWrYsKGqVKmiOnXq6Ouvv9bUqVPVq1cvm1sa2DZt2qQbbrhBLVq00IIFC3TbbbcV2S1fp04du5sIlChXXHHFRR9L/v333/u5NbgYdvYntWrVOu9jLpdLO3bs8HmmXYKCzr7CxqkTo518knlhYaEGDx6s+fPn64orrtCECROKdRglzq20rk9W+c9//qOsrCz17t3be98LL7ygMWPGqKCgQNdff70++OADxcTE+DSXQ9rOsGrVKk2bNs17+5133lFsbKzWr1+vkJAQvfzyy/rHP/5BwXOZ7Not/8ADDygtLU0RERFF7j969Kj69++vt956y2/ZpUVmZqYSExPP+nJujNHu3btVvXp1m1oW2Lp27Wp3E3hvi8nO/mTnzp0+n2dJZdey7t27V0OGDNH8+fO1b9++s0ae8tUX40OHDumJJ57Q3LlzNW3aNHXo0EGSNGDAAH366afq27ev5syZo/79+2vmzJk+ySzNStNnxw4TJkzQX//6V+/tFStWaNSoUXruuefUqFEjPf300xozZowmTJjg01z28JyhbNmy+t///uc9BKFTp05q0qSJXnzxRUlSRkaG2rZtq6ysLDubGfDO98Xp1GP++uIUHBysPXv2nHXYwf79+xUfH6+CggK/5JYm53uNs7KyVKlSJX4dC2C8t8VDf+JsHTt2VGZmph5//HElJCSc1Z916dLFJzl33323srKydM011yg9PV3vvfeePvnkE915551atmyZWrdurR9++EHXX3+9fv/9d59knguff/hCpUqVNHfuXO+Q7oMHD1Z6err3wrmzZ8/WgAEDtHXrVp/msofnDJGRkTp06JC3g1qzZo369Onjfdzlcvn12juFhYWaNm2a9xejMy8Wt2DBAr9lW6lWrVrn3XDWqlXL5xvOnJwcGWNkjNHhw4dVpkwZ72OFhYWaPXs2x177yPkuHnvkyJEirzsCD+9t8djZn5SWvuSUd999V//85z+1c+dOrVy5UjVq1NCkSZNUq1YtnxUeZ1q2bJmWLl2q5s2b+2X+p8yZM0crVqxQrVq1dOWVVyomJkY5OTkaOXKkWrduLUkKDw/XsWPH/NqO8/0+npeXp7CwML9mW82O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/KzPw4EDB/ySW7t2bb355pvq0KFDkVG13nnnHY0bN07p6ek+z7z22mvVo0cP9e/fXxEREdq4caNq1aql/v37a+vWrZozZ47PM0+x4/O/YcOGIrfz8/O1bt06TZgwQS+88MIfXtPvUti1PknSu+++6z0nbOXKlapRo4YmTpyo2rVrq0uXLn7LtdqJEyfOeUHmO++8U4MGDfLLBbQpeC7SiRMndOLECb9f6dbj8cjj8XhHXfnggw+0fPly1atXT4888ojfrspthZiYmCJf9I8ePWr5BuXxxx/XtGnTdMstt5yz8Jg4caJfcsuVK6f//ve/atiwoV/mX5J06tRJxhi99957io2NlSRlZWXpnnvuUVBQkL766iubWxj4nn/+eY0aNUoNGjRQ5cqVzxq0wF+FO++tb1jRnzi5LznTqRP4z+e+++7zS25qaqr+/e9/66233tKNN96o2bNn66efftKgQYM0cuRI9e/f3+eZy5YtU8eOHXXPPfd4By9JT0/XihUrtHjxYrVs2dLnmaeUpM//V199pZdeekmLFi3y+bztWp/eeOMNjRo1SgMHDtTzzz+vH3/8UbVr19a0adM0ffp0LVy40C+5pQUFzzm8/fbb+v7773XllVfq7rvv1vDhwzV+/HgVFBTo+uuv18yZMxUXF+e3/OPHj2vjxo1n7YFwuVx+vaigv/3RRuR0/tqgVKhQQe+8847lI5786U9/0sSJE/0yak9JU65cOa1atUrJyclF7t+wYYOuuuoqHTlyxKaWOUdMTIwmTpyo3r17W5rLe1t8dvYnTu1LSgpjjMaOHavU1FTvsOxut1tDhgzRmDFj/Ja7Y8cOpaamasOGDTpy5IhatGihp5566qzPpa+VpM//tm3b1KxZMx09etSyTH9LSkrS2LFjvRcgPbXHcNOmTUpJSdH+/fvtbqJPrFmzRi1btjzvYZB5eXn6/PPPdfvtt/s22OdnBQW4559/3pQtW9Z06NDBxMbGmkceecTEx8ebcePGmRdffNFUq1bNPPLII37L//rrr02FChWMy+U6awoKCvJbrtV69eplpk6davmJ1wkJCWbLli2WZhpjzPz5803btm3NwoULzf79+012dnaRyUliYmLM8uXLz7p/2bJlJiYmxoYWOU/lypVNRkaG5bm8t8VjZ3/i9L7k9O3mmdtTq7eveXl55scffzSrV682hw8f9mtWr169zFtvvWXLoCV2fP7PfC8PHTpkNm/ebO644w7TrFkzn+acL9Oq9alMmTLegUZOH5AiIyPDlClTxm+5VgsKCioy6lxERIR3WY0x5rfffvPLNopBC84wbdo0TZ06VT179tTatWvVpk0bzZo1y3ul7CZNmuiRRx7xW37//v11++23a9SoURccp9zXtm7dqoULF57zvJZRo0b5PM/tdmvcuHHq27evqlSpovbt2yslJUXt27f36QXMzvTEE08oLS1Nr732mqXXaujQoYMk6frrry+Saxw4aEHnzp310EMPaerUqd6LLq5evVqPPPKIbrvtNr9mW70e22XAgAF69dVX//C6Ur5m53sbiOzsT+zqSyRrPocxMTHas2ePKlWqpOjo6HNuz63avoaFhSkpKcmvGadnpaam6sEHH7S075Ts+fyf6701xigxMVEzZ870WU5JWJ9q1aql9evXq0aNGkXunzNnjho1auSXTDuYMw4sO/P2+e67XBzSdga3261t27Z5LxTndru1ceNG7wl6v/zyi2rVqnXWVat9JTIyUuvWrbP05N/Jkyfr0UcfVYUKFRQfH3/W+QD+vCL3L7/8oiVLlmjx4sVavHixMjIylJCQoJ9//tlnGWee1LhgwQLFxsaqcePGZx3H/sknn/gs93SLFy++4ON2XrHb1w4dOqT77rtP//nPf7yvb35+vrp06aJp06YpKirKL7l2rsdW69atmxYsWKC4uDhL12O73ttAZWd/YkdfIln3OVy8eLF3MBgrt6/FOUneX59DyZq+80x2fP7PfG+DgoJUsWJF1a1b13t+mq9y7FifTjdlyhQ9++yzGj9+vPr06aMpU6Zo+/btSk1N1ZQpU3TnnXf6JddqQUFB+u2337yjDZ5++J508sL0VapU8XlhyR6eM+Tn5xcZHSIsLKzIl4mQkBC//lr017/+VYsWLbK0k3r++ef1wgsv6KmnnrIs85SYmBjFxcUpJiZG0dHRCgkJUcWKFX2aceZGuFu3bj6d/8Vo3769Dh06pKlTp3qH80xKSlKfPn38/iUxPT1dmZmZZ32p8ucvcp9//rm2bdvmHaUoKSlJdevW9UveKXaux3a8xv4Ynehicu14bwOVnf2JHX2JZN3n8PQvne3bt9fSpUv1r3/9S9u3b9dHH32kqlWr6t1331WtWrV8mnux22t/H0FgRd95Jjs+/6fe59O3sQcPHvSO7OWrbaxd69PpHnzwQZUtW1YjRoxQbm6u7rrrLlWtWlVpaWmOKXbsRMFzDunp6frtt98kndyt9r///c97Mp6/Txp77bXX1KNHDy1duvScw83+7W9/83nmwYMH1aNHD5/P90KGDx+uRYsWad26dWrUqJHat2+voUOH6tprr1VMTIxPs95++23v/48dOyaPx6Ny5cpJknbt2qXPPvtMjRo10s033+zT3NOtXbtWf/7zn1WmTBnvoQATJ07U2LFjNW/ePLVo0cLnmTt27FC3bt30ww8/yOVyeXcRn+qI/Vm4T506VRMnTtTWrVslSfXq1dPAgQP14IMP+i3TjvXYrtf49ddft2U9lux5bwOZXf2JHX2JZM/n8OOPP1avXr109913a926dcrLy5MkZWdna+zYsZo9e7bPsk7vT2bMmKGePXue83lPPvmkzzJPZ2XfeS5Wf/537Nih7t27a+PGjZZtY61cn0537NgxdevWTXfffbdyc3O1adMmLV++XNWqVfNLnp1s2S76/KygAHfqhM7znejp7xM+p0yZYkJCQkz58uVNjRo1TM2aNb1TrVq1/JL5wAMPmDfeeMMv8z4fl8tlKlWqZFJTUy0dRODGG2/0LuvBgwdN5cqVTbVq1UyZMmXM66+/7rfcq6++2vTu3dvk5+d778vPzzf33Xefueaaa/yS2blzZ9OlSxfz+++/m/Lly5v09HSzdOlS07p1a7NkyRK/ZBpjzMiRI025cuXM0KFDzeeff24+//xzM3ToUFO+fHm/XD35FDvWY7teY7vWY7ve20BlZ39iR19ijD2fw+bNm5vp06cbY4qe7P3999+bypUr+y03KirKzJ49+6z7Bw0aZOLj4/2SaVffaYw9n/8zt7E//vij37exdq1Pdm3XrWbXdpGC5wy7du26qMlfKleubF544QVTWFjot4wzjR071lSoUMHcd9995uWXXzZpaWlFJn9Yv369SUtLM926dTMVKlQwVapUMT179jT/+te//LoRj4uLM5s2bTLGGDN58mTTtGlTU1hYaGbNmmUaNmzot9wyZcqYzZs3n3X/jz/+aMqWLeuXzLi4OLNhwwZjjDGRkZHmf//7nzHm5IhxzZs390umMcZUqFDBvP/++2fd//7775u4uDi/5dqxHtv1Gtu1Htv13gYqO/sTO/oSY+z5HJYtW9bs3LnTGFP0C+r27duN2+32S6Yxxnz55ZcmKirKLF261Hvf448/bhISEs65vfcFu/pOY+z5/NuxjbVrfbJru241u7aLFDyn2bBhQ7E6h02bNhX5xd4XYmJiLB9u8vRf/s6c/PlL4OnWr19v7rvvPhMSEuLXPWhly5Y1P/30kzHGmB49ephnn33WGGNMZmam3woPY4ypVKmSmTt37ln3z5kzx1SqVMkvmdHR0WbHjh3GGGNq165tFixYYIwxZtu2bX5d1qioqHMOmbxlyxYTFRXlt1w71mO7XmO71mO73ttAZHd/YkdfYow9n8NatWqZb775xhhT9Avq9OnTTaNGjfySecp7771nYmJizNq1a82jjz5qqlSpYumeF6v6TmPs+fzbsY21a32ya7tuJTu3ixQ8pwkKCjL79u276OefOXa4LwwcONC88MILPp1nSeTxeMx///tfM378eHPrrbeamJgYExwcbK644gozcOBAv+UmJyebtLQ0k5mZaSIjI82KFSuMMcasXbvWr7uq+/fvb6pVq2ZmzpxpMjMzTWZmppkxY4apVq2aGTBggF8yr776avPpp58aY4zp2bOn+fOf/2yWLVtm7r33XtO4cWO/ZBpz8hfOQYMGnXX/E088YR577DG/5drBrtfYrvW4NL23l8vu/qS09CXGnNyrlJSUZFatWmUiIiLM0qVLzb///W9TsWJF88orr/g9/x//+Idxu92mWrVqZuvWrX7NsqvvNMaez78d21i71ie7tutWsnO7yKAFpzHGaOTIkQoPD7+o5/tjKNHCwkK9+OKLmjt3rpo2bXrWiaYTJkzwSc7gwYM1ZswYlStXToMHDz7v81wul8aPH++TzNPFxsbqyJEjatasmdq3b6++ffvqmmuuUXR0tM+zTjdq1CjdddddGjRokG644Qa1bdtWkjRv3jxdccUVfst9+eWX5XK5dO+996qgoECSFBoaqkcffVTjxo3zS+aIESO8V6F+7rnn1LlzZ11zzTWKi4vTBx984NOs09chl8ulKVOmaN68ebryyislnbxWQ2Zmpu69916f59q5Hlv5Gp/OyvXYrvf2YnTo0EE7duzQjh07LM/+I3b3J1b1JZL9n8OhQ4fK4/HohhtuUG5urq699lq53W4NGTJE/fv392nW+ZavYsWKatGihV5//XXvfb58jU+xuu+0+/NvxzbWyvXpdHZ9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\n"
     },
     "metadata": {}
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "['man in white shirt on a boat in a small boat.']"
      ]
     },
     "metadata": {},
     "execution_count": 41
    }
   ],
   "source": [
    "import re\n",
    "from fastBPE import fastBPE\n",
    "from sacremoses import MosesDetokenizer, MosesTokenizer\n",
    "\n",
    "# `MosesTokenizer` 和 `MosesDetokenizer` 是来自 `sacremoses` 库的工具，用于自然语言处理中的分词（Tokenization）和去标记化（Detokenization）。这些工具主要用于对文本进行预处理和后处理，通常在处理自然语言处理任务时会用到。\n",
    "#\n",
    "# ### MosesTokenizer：\n",
    "# - **作用**：将原始文本分割成单词和标点符号。\n",
    "# - **特点**：基于 Moses 翻译工具中使用的分词方法。\n",
    "# - **功能**：\n",
    "#   - 将句子分割成单词和标点符号。\n",
    "#   - 处理缩写、连字符、标点等特殊情况。\n",
    "#   - 对文本进行标记化，方便后续处理。\n",
    "#\n",
    "# ### MosesDetokenizer：\n",
    "# - **作用**：将分词后的文本重新组合成原始的句子。\n",
    "# - **特点**：用于对分词后的文本进行还原，使其恢复为可读的句子形式。\n",
    "# - **功能**：\n",
    "#   - 将分词后的单词和标点符号重新组合成句子。\n",
    "#   - 处理分词后的标点、缩写等情况，使得结果更加自然和可读。\n",
    "#\n",
    "# 这些工具通常在文本预处理和后处理过程中使用，对输入的文本进行标记化和去标记化，是一种常用的处理方式。在自然语言处理任务中，对文本进行正确的分词和还原是很重要的，而 `MosesTokenizer` 和 `MosesDetokenizer` 提供了方便、高效的工具来处理这些任务。\n",
    "\n",
    "class Translator:\n",
    "    def __init__(self, model, src_tokenizer, trg_tokenizer):\n",
    "        self.bpe = fastBPE(\"./wmt16/bpe.10000\", \"./wmt16/vocab\")\n",
    "        self.mose_tokenizer = MosesTokenizer(lang=\"de\")\n",
    "        self.mose_detokenizer = MosesDetokenizer(lang=\"en\")\n",
    "        self.model = model\n",
    "        self.model.eval()\n",
    "        self.src_tokenizer = src_tokenizer\n",
    "        self.trg_tokenizer = trg_tokenizer\n",
    "        self.pattern = re.compile(r'(@@ )|(@@ ?$)')\n",
    "\n",
    "    def draw_attention_map(self, attn_scores, cross_attn_scores, src_words_list, trg_words_list):\n",
    "        \"\"\"绘制注意力热力图\n",
    "        attn_scores (numpy.ndarray): 表示自注意力机制（self-attention）分数。\n",
    "        cross_attn_scores (numpy.ndarray): 表示交叉注意力机制的注意力分数。\n",
    "        src_words_list (list): 源语言句子的单词列表。\n",
    "        trg_words_list (list): 目标语言句子的单词列表。\n",
    "        \"\"\"\n",
    "        assert len(attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, target sequence length], but got {attn_scores.shape}\"\n",
    "        attn_scores = attn_scores[:, :len(trg_words_list), :len(trg_words_list)]\n",
    "\n",
    "        assert len(cross_attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, source sequence length], but got {cross_attn_scores.shape}\"\n",
    "        cross_attn_scores = cross_attn_scores[:, :len(trg_words_list), :len(src_words_list)]\n",
    "\n",
    "        num_heads, trg_len, src_len = cross_attn_scores.shape\n",
    "\n",
    "        fig = plt.figure(figsize=(10, 5), constrained_layout=True) # constrained_layout=True 自动调整子图参数，使之填充整个图像区域\n",
    "        grid = plt.GridSpec(trg_len, trg_len + src_len, wspace=0.1, hspace=0.1)# wspace,hspace 控制子图之间的间距\n",
    "        #下面是attn_scores的热力图\n",
    "        self_map = fig.add_subplot(grid[:,:trg_len]) #  添加子图\n",
    "        self_map.matshow(attn_scores.mean(dim=0), cmap='viridis') # 绘制热力图，cmap表示颜色,dim=0表示对第0维求均值\n",
    "        self_map.set_yticks(range(trg_len), trg_words_list, fontsize=10)\n",
    "        self_map.set_xticks(range(trg_len), [\"[BOS]\"] + trg_words_list[:-1], rotation=90)\n",
    "        #下面是cross_attn_scores的热力图\n",
    "        cross_map = fig.add_subplot(grid[:, trg_len:])\n",
    "        cross_map.matshow(cross_attn_scores.mean(dim=0), cmap='viridis')\n",
    "        cross_map.set_yticks(range(trg_len), [], fontsize=6)\n",
    "        cross_map.set_xticks(range(src_len), src_words_list, rotation=90)\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "    def draw_attention_maps(self, attn_scores, cross_attn_scores, src_words_list, trg_words_list, heads_list):\n",
    "        \"\"\"绘制注意力热力图\n",
    "\n",
    "        Args:\n",
    "            - scores (numpy.ndarray): shape = [source sequence length, target sequence length]\n",
    "        \"\"\"\n",
    "        assert len(attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, target sequence length], but got {attn_scores.shape}\"\n",
    "        attn_scores = attn_scores[:, :len(trg_words_list), :len(trg_words_list)]\n",
    "\n",
    "        assert len(cross_attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, source sequence length], but got {cross_attn_scores.shape}\"\n",
    "        cross_attn_scores = cross_attn_scores[:, :len(trg_words_list), :len(src_words_list)]\n",
    "        # cross_attn_scores = cross_attn_scores[:, :len(src_words_list), :len(src_words_list)]\n",
    "\n",
    "        num_heads, trg_len, src_len = cross_attn_scores.shape\n",
    "        fig, axes = plt.subplots(2, len(heads_list), figsize=(5 * len(heads_list), 10))\n",
    "        for i, heads_idx in enumerate(heads_list):\n",
    "            axes[0, i].matshow(attn_scores[heads_idx], cmap='viridis')\n",
    "            axes[0, i].set_yticks(range(trg_len), trg_words_list)\n",
    "            axes[0, i].set_xticks(range(trg_len), [\"[BOS]\"] + trg_words_list[:-1], rotation=90)\n",
    "            axes[0, i].set_title(f\"head {heads_idx}\")\n",
    "            axes[1, i].matshow(cross_attn_scores[heads_idx], cmap='viridis')\n",
    "            axes[1, i].set_yticks(range(trg_len), trg_words_list)\n",
    "            axes[1, i].set_xticks(range(src_len), src_words_list, rotation=90)\n",
    "            axes[1, i].set_title(f\"head {heads_idx}\")\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "\n",
    "    def __call__(self, sentence_list, heads_list=None, layer_idx=-1):\n",
    "        # 将输入句子列表转换为小写，并使用 MosesTokenizer 进行分词处理。\n",
    "        sentence_list = [\" \".join(self.mose_tokenizer.tokenize(s.lower())) for s in sentence_list]\n",
    "        # 将分词后的结果进行 BPE 编码，得到 tokens_list。\n",
    "        tokens_list = [s.split() for s in self.bpe.apply(sentence_list)]\n",
    "        # 使用 src_tokenizer 对 tokens_list 进行编码，同时添加起始标记 ([BOS]) 和结束标记 ([EOS])。\n",
    "        encoder_input, attn_mask = self.src_tokenizer.encode(\n",
    "            tokens_list,\n",
    "            add_bos=True,\n",
    "            add_eos=True,\n",
    "            return_mask=True,\n",
    "            )\n",
    "        encoder_input = torch.Tensor(encoder_input).to(dtype=torch.int64)\n",
    "        # 使用模型的 infer 方法对编码器输入进行推理，得到输出结果 outputs\n",
    "        outputs = model.infer(encoder_inputs=encoder_input, encoder_inputs_mask=attn_mask)\n",
    "\n",
    "        preds = outputs.preds.numpy()\n",
    "        # 使用目标语言的 trg_tokenizer 对预测序列进行解码，得到解码后的目标语言句子列表 trg_decoded。\n",
    "        trg_decoded = self.trg_tokenizer.decode(preds, split=True, remove_eos=False, remove_bos=False, remove_pad=False)\n",
    "        # 使用源语言的 src_tokenizer 对编码器输入进行解码，得到解码后的源语言句子列表 src_decoded。为下面绘制热力图做准备。\n",
    "        src_decoded = self.src_tokenizer.decode(\n",
    "            encoder_input.numpy(),\n",
    "            split=True,\n",
    "            remove_bos=False,\n",
    "            remove_eos=False\n",
    "            )\n",
    "\n",
    "        # post processed attn scores\n",
    "        # outputs.decoder_attentions[-1]  # the last layer of self-attention scores\n",
    "\n",
    "        # draw the attention map of the last decoder block\n",
    "        for attn_score, cross_attn_score, src, trg in zip(\n",
    "            outputs.decoder_self_attn_scores[layer_idx], outputs.decoder_cross_attn_scores[layer_idx], src_decoded, trg_decoded):\n",
    "            if heads_list is None:# 如果没有指定heads_list，就画单个热力图\n",
    "                self.draw_attention_map(\n",
    "                    attn_score,\n",
    "                    cross_attn_score,\n",
    "                    src,\n",
    "                    trg,\n",
    "                )\n",
    "            else:# 如果指定了heads_list，就画多个热力图\n",
    "                self.draw_attention_maps(\n",
    "                    attn_score,\n",
    "                    cross_attn_score,\n",
    "                    src,\n",
    "                    trg,\n",
    "                    heads_list=heads_list,\n",
    "                    )\n",
    "        return [self.mose_detokenizer.tokenize(self.pattern.sub(\"\", s).split()) for s in self.trg_tokenizer.decode(preds)] #将解码后的目标语言句子列表返回，并使用 mose_detokenizer 进行去标记化，最终得到翻译后的结果。\n",
    "\n",
    "\n",
    "# sentence_list = [\n",
    "#     \"Mann in einem kleinen weißen Boot auf einem See.\",  # Man in a small white boat on a lake.\n",
    "#     \"Ein Mann mit einem Eimer und ein Mädchen mit einem Hut am Strand.\", # A man with a bucket and a girl in a hat on the beach.\n",
    "#     \"Drei Männer auf Pferden während eines Rennens.\",  # Three men on horses during a race.\n",
    "#     \"Ein Mann und eine Frau essen zu Abend\",  # 一个男人和一个女人在吃晚餐\n",
    "# ]\n",
    "sentence_list = [\n",
    "    \"Mann in einem kleinen weißen Boot auf einem See.\",  # Man in a small white boat on a lake.\n",
    "    # \"Ein Mann mit einem Eimer und ein Mädchen mit einem Hut am Strand.\", # A man with a bucket and a girl in a hat on the beach.\n",
    "    # \"Drei Männer auf Pferden während eines Rennens.\",  # Three men on horses during a race.\n",
    "    # \"Ein Mann und eine Frau essen zu Abend\",  # 一个男人和一个女人在吃晚餐\n",
    "]\n",
    "\n",
    "# load checkpoints\n",
    "model = TransformerModel(config)\n",
    "model.load_state_dict(state_dict)\n",
    "translator = Translator(model.cpu(), tokenizer, tokenizer)\n",
    "translator(\n",
    "    sentence_list,\n",
    "    layer_idx=-1,\n",
    "    # heads_list=[0, 1, 2, 3, 4, 5, 6, 7]\n",
    "    )\n"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "!ls checkpoints"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "e0WkUQuUe-Cy",
    "outputId": "433e3d70-4f34-4376-e4a3-1b8834e71f88"
   },
   "execution_count": null,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "translate-transformer-not-share\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# prompt: 把best.ckpt复制到云盘内\n",
    "\n",
    "!cp -r checkpoints/translate-transformer-not-share/best.ckpt /content/drive/MyDrive/transformer-de-en\n"
   ],
   "metadata": {
    "id": "_1ZLtdahywWf"
   },
   "execution_count": null,
   "outputs": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "name": "python3",
   "language": "python",
   "display_name": "Python 3 (ipykernel)"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4,
  "colab": {
   "provenance": [],
   "gpuType": "T4"
  },
  "accelerator": "GPU",
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "55146ac7395840dca847e0f63aa80561": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_abb9bc76c0ce4634bd340be20c074b8c",
       "IPY_MODEL_a89d790df59245fcaf26335d4903c9b4",
       "IPY_MODEL_f837255914194dc792cf1514dee03dc5"
      ],
      "layout": "IPY_MODEL_46a51a1719d8444eb5877720253d3c37"
     }
    },
    "abb9bc76c0ce4634bd340be20c074b8c": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_013d2a6b90144432bbfb15136235faba",
      "placeholder": "​",
      "style": "IPY_MODEL_2b82e42c34804b13bc83be4c7009d4e2",
      "value": ""
     }
    },
    "a89d790df59245fcaf26335d4903c9b4": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "success",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_9233439bdd6746c28ad4a20ef47799f6",
      "max": 280,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_0e10428326bc436e8dbc079e89570bde",
      "value": 280
     }
    },
    "f837255914194dc792cf1514dee03dc5": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_0fe4ed7283c04496affe727c235d31c4",
      "placeholder": "​",
      "style": "IPY_MODEL_0c0c7229347b49ffbb9a0f7bd41199b9",
      "value": " 21020/? [21:30&lt;00:00, 15.32it/s, epoch=19, loss=2.61, val_loss=3.41]"
     }
    },
    "46a51a1719d8444eb5877720253d3c37": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "013d2a6b90144432bbfb15136235faba": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "2b82e42c34804b13bc83be4c7009d4e2": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "9233439bdd6746c28ad4a20ef47799f6": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "0e10428326bc436e8dbc079e89570bde": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "0fe4ed7283c04496affe727c235d31c4": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "0c0c7229347b49ffbb9a0f7bd41199b9": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "7190c928edb040c3a523cdd2e3f45572": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_990741ff083d457587665d8f620b69c3",
       "IPY_MODEL_00fe6a550df547cea5b1e0d84c1a3f5f",
       "IPY_MODEL_fab8b1e3187841ee894b92859fa4821b"
      ],
      "layout": "IPY_MODEL_f9b0d916e1a64c2a9f384e42f3d5dcab"
     }
    },
    "990741ff083d457587665d8f620b69c3": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_a78199650ecc47bd81f482a2671b563a",
      "placeholder": "​",
      "style": "IPY_MODEL_673bd3773a374261a01cfc48081c27f3",
      "value": "100%"
     }
    },
    "00fe6a550df547cea5b1e0d84c1a3f5f": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "success",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_83f345f7424c43929d7a304cbfb6d0fe",
      "max": 9714,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_58d69d086f644e89937347e1f9261d6f",
      "value": 9714
     }
    },
    "fab8b1e3187841ee894b92859fa4821b": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_8cf462fa996c41b18fb4571e0a757363",
      "placeholder": "​",
      "style": "IPY_MODEL_434ac4c51db74681942d2d3bd869b1df",
      "value": " 9714/9714 [00:00&lt;00:00, 236744.37it/s]"
     }
    },
    "f9b0d916e1a64c2a9f384e42f3d5dcab": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "a78199650ecc47bd81f482a2671b563a": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "673bd3773a374261a01cfc48081c27f3": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "83f345f7424c43929d7a304cbfb6d0fe": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "58d69d086f644e89937347e1f9261d6f": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "8cf462fa996c41b18fb4571e0a757363": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "434ac4c51db74681942d2d3bd869b1df": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "c9db4cd83b7b4f53a0fa93b8c365debd": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_a972aba7913b48c6973ae27bff5b9d1d",
       "IPY_MODEL_22aa774ab39e4fd78b0ff08d430d2b5d",
       "IPY_MODEL_9a01f02fb99b4b81ac487d23bc5daf4a"
      ],
      "layout": "IPY_MODEL_19f846338f6b48c9930b58575641e5b8"
     }
    },
    "a972aba7913b48c6973ae27bff5b9d1d": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_7d293204dc1b4d97bb5ee670787c7b7d",
      "placeholder": "​",
      "style": "IPY_MODEL_ea8fa440f320435aa75c9bef75bc3f08",
      "value": ""
     }
    },
    "22aa774ab39e4fd78b0ff08d430d2b5d": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "success",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_4e64bcb87e584beda0408a0aa5a659ae",
      "max": 1,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_b077d7562d814df5b6d839464dd4122f",
      "value": 1
     }
    },
    "9a01f02fb99b4b81ac487d23bc5daf4a": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_5a28babf4e2e4393bdc1a093034885ab",
      "placeholder": "​",
      "style": "IPY_MODEL_be07d518c21149d196c610fd57b1eaec",
      "value": " 957/? [00:18&lt;00:00, 51.31it/s]"
     }
    },
    "19f846338f6b48c9930b58575641e5b8": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "7d293204dc1b4d97bb5ee670787c7b7d": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "ea8fa440f320435aa75c9bef75bc3f08": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "4e64bcb87e584beda0408a0aa5a659ae": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": "20px"
     }
    },
    "b077d7562d814df5b6d839464dd4122f": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "5a28babf4e2e4393bdc1a093034885ab": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "be07d518c21149d196c610fd57b1eaec": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "40f7e3754ad44b33acfe0a80b3648a3a": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_b1e51beac50f4947a9dd235d8aa87603",
       "IPY_MODEL_8cd9e9702e114ebc856f56975544ede8",
       "IPY_MODEL_b7c7a718a8654956947108969557ca83"
      ],
      "layout": "IPY_MODEL_20a27d1ec76f4d85b1ffe99401fbb5b7"
     }
    },
    "b1e51beac50f4947a9dd235d8aa87603": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_e0ddf3970b3e4248bc87d4649fa9c448",
      "placeholder": "​",
      "style": "IPY_MODEL_d7bdde54e2534c49a207540bd258d3ae",
      "value": "  9%"
     }
    },
    "8cd9e9702e114ebc856f56975544ede8": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "danger",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_22484000400842f0b6e245dc610acbba",
      "max": 128,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_a66a5b4ff37d4dafa4ba3081aa1359f4",
      "value": 12
     }
    },
    "b7c7a718a8654956947108969557ca83": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_0b7866c29e5f4afe9b92de15f1b88c3e",
      "placeholder": "​",
      "style": "IPY_MODEL_333e84a304184c57af1821620ae80f6a",
      "value": " 12/128 [00:00&lt;00:01, 73.96it/s]"
     }
    },
    "20a27d1ec76f4d85b1ffe99401fbb5b7": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "e0ddf3970b3e4248bc87d4649fa9c448": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "d7bdde54e2534c49a207540bd258d3ae": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "22484000400842f0b6e245dc610acbba": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "a66a5b4ff37d4dafa4ba3081aa1359f4": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "0b7866c29e5f4afe9b92de15f1b88c3e": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "333e84a304184c57af1821620ae80f6a": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
